github.com/pure-x-eth/consensus_tm@v0.0.0-20230502163723-e3c2ff987250/p2p/peer.go (about)

     1  package p2p
     2  
     3  import (
     4  	"fmt"
     5  	"net"
     6  	"reflect"
     7  	"time"
     8  
     9  	"github.com/gogo/protobuf/proto"
    10  
    11  	"github.com/pure-x-eth/consensus_tm/libs/cmap"
    12  	"github.com/pure-x-eth/consensus_tm/libs/log"
    13  	"github.com/pure-x-eth/consensus_tm/libs/service"
    14  
    15  	tmconn "github.com/pure-x-eth/consensus_tm/p2p/conn"
    16  )
    17  
    18  //go:generate ../scripts/mockery_generate.sh Peer
    19  
    20  const metricsTickerDuration = 10 * time.Second
    21  
    22  // Peer is an interface representing a peer connected on a reactor.
    23  type Peer interface {
    24  	service.Service
    25  	FlushStop()
    26  
    27  	ID() ID               // peer's cryptographic ID
    28  	RemoteIP() net.IP     // remote IP of the connection
    29  	RemoteAddr() net.Addr // remote address of the connection
    30  
    31  	IsOutbound() bool   // did we dial the peer
    32  	IsPersistent() bool // do we redial this peer when we disconnect
    33  
    34  	CloseConn() error // close original connection
    35  
    36  	NodeInfo() NodeInfo // peer's info
    37  	Status() tmconn.ConnectionStatus
    38  	SocketAddr() *NetAddress // actual address of the socket
    39  
    40  	// Deprecated: entities looking to act as peers should implement SendEnvelope instead.
    41  	// Send will be removed in v0.37.
    42  	Send(byte, []byte) bool
    43  
    44  	// Deprecated: entities looking to act as peers should implement TrySendEnvelope instead.
    45  	// TrySend will be removed in v0.37.
    46  	TrySend(byte, []byte) bool
    47  
    48  	Set(string, interface{})
    49  	Get(string) interface{}
    50  
    51  	SetRemovalFailed()
    52  	GetRemovalFailed() bool
    53  }
    54  
    55  type EnvelopeSender interface {
    56  	SendEnvelope(Envelope) bool
    57  	TrySendEnvelope(Envelope) bool
    58  }
    59  
    60  // EnvelopeSendShim implements a shim to allow the legacy peer type that
    61  // does not implement SendEnvelope to be used in places where envelopes are
    62  // being sent. If the peer implements the *Envelope methods, then they are used,
    63  // otherwise, the message is marshaled and dispatched to the legacy *Send.
    64  //
    65  // Deprecated: Will be removed in v0.37.
    66  func SendEnvelopeShim(p Peer, e Envelope, lg log.Logger) bool {
    67  	if es, ok := p.(EnvelopeSender); ok {
    68  		return es.SendEnvelope(e)
    69  	}
    70  	msg := e.Message
    71  	if w, ok := msg.(Wrapper); ok {
    72  		msg = w.Wrap()
    73  	}
    74  	msgBytes, err := proto.Marshal(msg)
    75  	if err != nil {
    76  		lg.Error("marshaling message to send", "error", err)
    77  		return false
    78  	}
    79  	return p.Send(e.ChannelID, msgBytes)
    80  }
    81  
    82  // EnvelopeTrySendShim implements a shim to allow the legacy peer type that
    83  // does not implement TrySendEnvelope to be used in places where envelopes are
    84  // being sent. If the peer implements the *Envelope methods, then they are used,
    85  // otherwise, the message is marshaled and dispatched to the legacy *Send.
    86  //
    87  // Deprecated: Will be removed in v0.37.
    88  func TrySendEnvelopeShim(p Peer, e Envelope, lg log.Logger) bool {
    89  	if es, ok := p.(EnvelopeSender); ok {
    90  		return es.SendEnvelope(e)
    91  	}
    92  	msg := e.Message
    93  	if w, ok := msg.(Wrapper); ok {
    94  		msg = w.Wrap()
    95  	}
    96  	msgBytes, err := proto.Marshal(msg)
    97  	if err != nil {
    98  		lg.Error("marshaling message to send", "error", err)
    99  		return false
   100  	}
   101  	return p.TrySend(e.ChannelID, msgBytes)
   102  }
   103  
   104  //----------------------------------------------------------
   105  
   106  // peerConn contains the raw connection and its config.
   107  type peerConn struct {
   108  	outbound   bool
   109  	persistent bool
   110  	conn       net.Conn // source connection
   111  
   112  	socketAddr *NetAddress
   113  
   114  	// cached RemoteIP()
   115  	ip net.IP
   116  }
   117  
   118  func newPeerConn(
   119  	outbound, persistent bool,
   120  	conn net.Conn,
   121  	socketAddr *NetAddress,
   122  ) peerConn {
   123  
   124  	return peerConn{
   125  		outbound:   outbound,
   126  		persistent: persistent,
   127  		conn:       conn,
   128  		socketAddr: socketAddr,
   129  	}
   130  }
   131  
   132  // ID only exists for SecretConnection.
   133  // NOTE: Will panic if conn is not *SecretConnection.
   134  func (pc peerConn) ID() ID {
   135  	return PubKeyToID(pc.conn.(*tmconn.SecretConnection).RemotePubKey())
   136  }
   137  
   138  // Return the IP from the connection RemoteAddr
   139  func (pc peerConn) RemoteIP() net.IP {
   140  	if pc.ip != nil {
   141  		return pc.ip
   142  	}
   143  
   144  	host, _, err := net.SplitHostPort(pc.conn.RemoteAddr().String())
   145  	if err != nil {
   146  		panic(err)
   147  	}
   148  
   149  	ips, err := net.LookupIP(host)
   150  	if err != nil {
   151  		panic(err)
   152  	}
   153  
   154  	pc.ip = ips[0]
   155  
   156  	return pc.ip
   157  }
   158  
   159  // peer implements Peer.
   160  //
   161  // Before using a peer, you will need to perform a handshake on connection.
   162  type peer struct {
   163  	service.BaseService
   164  
   165  	// raw peerConn and the multiplex connection
   166  	peerConn
   167  	mconn *tmconn.MConnection
   168  
   169  	// peer's node info and the channel it knows about
   170  	// channels = nodeInfo.Channels
   171  	// cached to avoid copying nodeInfo in hasChannel
   172  	nodeInfo NodeInfo
   173  	channels []byte
   174  
   175  	// User data
   176  	Data *cmap.CMap
   177  
   178  	metrics       *Metrics
   179  	metricsTicker *time.Ticker
   180  	mlc           *metricsLabelCache
   181  
   182  	// When removal of a peer fails, we set this flag
   183  	removalAttemptFailed bool
   184  }
   185  
   186  type PeerOption func(*peer)
   187  
   188  func newPeer(
   189  	pc peerConn,
   190  	mConfig tmconn.MConnConfig,
   191  	nodeInfo NodeInfo,
   192  	reactorsByCh map[byte]Reactor,
   193  	msgTypeByChID map[byte]proto.Message,
   194  	chDescs []*tmconn.ChannelDescriptor,
   195  	onPeerError func(Peer, interface{}),
   196  	mlc *metricsLabelCache,
   197  	options ...PeerOption,
   198  ) *peer {
   199  	p := &peer{
   200  		peerConn:      pc,
   201  		nodeInfo:      nodeInfo,
   202  		channels:      nodeInfo.(DefaultNodeInfo).Channels,
   203  		Data:          cmap.NewCMap(),
   204  		metricsTicker: time.NewTicker(metricsTickerDuration),
   205  		metrics:       NopMetrics(),
   206  		mlc:           mlc,
   207  	}
   208  
   209  	p.mconn = createMConnection(
   210  		pc.conn,
   211  		p,
   212  		reactorsByCh,
   213  		msgTypeByChID,
   214  		chDescs,
   215  		onPeerError,
   216  		mConfig,
   217  	)
   218  	p.BaseService = *service.NewBaseService(nil, "Peer", p)
   219  	for _, option := range options {
   220  		option(p)
   221  	}
   222  
   223  	return p
   224  }
   225  
   226  // String representation.
   227  func (p *peer) String() string {
   228  	if p.outbound {
   229  		return fmt.Sprintf("Peer{%v %v out}", p.mconn, p.ID())
   230  	}
   231  
   232  	return fmt.Sprintf("Peer{%v %v in}", p.mconn, p.ID())
   233  }
   234  
   235  //---------------------------------------------------
   236  // Implements service.Service
   237  
   238  // SetLogger implements BaseService.
   239  func (p *peer) SetLogger(l log.Logger) {
   240  	p.Logger = l
   241  	p.mconn.SetLogger(l)
   242  }
   243  
   244  // OnStart implements BaseService.
   245  func (p *peer) OnStart() error {
   246  	if err := p.BaseService.OnStart(); err != nil {
   247  		return err
   248  	}
   249  
   250  	if err := p.mconn.Start(); err != nil {
   251  		return err
   252  	}
   253  
   254  	go p.metricsReporter()
   255  	return nil
   256  }
   257  
   258  // FlushStop mimics OnStop but additionally ensures that all successful
   259  // SendEnvelope() calls will get flushed before closing the connection.
   260  // NOTE: it is not safe to call this method more than once.
   261  func (p *peer) FlushStop() {
   262  	p.metricsTicker.Stop()
   263  	p.BaseService.OnStop()
   264  	p.mconn.FlushStop() // stop everything and close the conn
   265  }
   266  
   267  // OnStop implements BaseService.
   268  func (p *peer) OnStop() {
   269  	p.metricsTicker.Stop()
   270  	p.BaseService.OnStop()
   271  	if err := p.mconn.Stop(); err != nil { // stop everything and close the conn
   272  		p.Logger.Debug("Error while stopping peer", "err", err)
   273  	}
   274  }
   275  
   276  //---------------------------------------------------
   277  // Implements Peer
   278  
   279  // ID returns the peer's ID - the hex encoded hash of its pubkey.
   280  func (p *peer) ID() ID {
   281  	return p.nodeInfo.ID()
   282  }
   283  
   284  // IsOutbound returns true if the connection is outbound, false otherwise.
   285  func (p *peer) IsOutbound() bool {
   286  	return p.peerConn.outbound
   287  }
   288  
   289  // IsPersistent returns true if the peer is persitent, false otherwise.
   290  func (p *peer) IsPersistent() bool {
   291  	return p.peerConn.persistent
   292  }
   293  
   294  // NodeInfo returns a copy of the peer's NodeInfo.
   295  func (p *peer) NodeInfo() NodeInfo {
   296  	return p.nodeInfo
   297  }
   298  
   299  // SocketAddr returns the address of the socket.
   300  // For outbound peers, it's the address dialed (after DNS resolution).
   301  // For inbound peers, it's the address returned by the underlying connection
   302  // (not what's reported in the peer's NodeInfo).
   303  func (p *peer) SocketAddr() *NetAddress {
   304  	return p.peerConn.socketAddr
   305  }
   306  
   307  // Status returns the peer's ConnectionStatus.
   308  func (p *peer) Status() tmconn.ConnectionStatus {
   309  	return p.mconn.Status()
   310  }
   311  
   312  // SendEnvelope sends the message in the envelope on the channel specified by the
   313  // envelope. Returns false if the connection times out trying to place the message
   314  // onto its internal queue.
   315  // Using SendEnvelope allows for tracking the message bytes sent and received by message type
   316  // as a metric which Send cannot support.
   317  func (p *peer) SendEnvelope(e Envelope) bool {
   318  	if !p.IsRunning() {
   319  		return false
   320  	} else if !p.hasChannel(e.ChannelID) {
   321  		return false
   322  	}
   323  	msg := e.Message
   324  	metricLabelValue := p.mlc.ValueToMetricLabel(msg)
   325  	if w, ok := msg.(Wrapper); ok {
   326  		msg = w.Wrap()
   327  	}
   328  	msgBytes, err := proto.Marshal(msg)
   329  	if err != nil {
   330  		p.Logger.Error("marshaling message to send", "error", err)
   331  		return false
   332  	}
   333  	res := p.Send(e.ChannelID, msgBytes)
   334  	if res {
   335  		p.metrics.MessageSendBytesTotal.With("message_type", metricLabelValue).Add(float64(len(msgBytes)))
   336  	}
   337  	return res
   338  }
   339  
   340  // Send msg bytes to the channel identified by chID byte. Returns false if the
   341  // send queue is full after timeout, specified by MConnection.
   342  // SendEnvelope replaces Send which will be deprecated in a future release.
   343  func (p *peer) Send(chID byte, msgBytes []byte) bool {
   344  	if !p.IsRunning() {
   345  		return false
   346  	} else if !p.hasChannel(chID) {
   347  		return false
   348  	}
   349  	res := p.mconn.Send(chID, msgBytes)
   350  	if res {
   351  		labels := []string{
   352  			"peer_id", string(p.ID()),
   353  			"chID", fmt.Sprintf("%#x", chID),
   354  		}
   355  		p.metrics.PeerSendBytesTotal.With(labels...).Add(float64(len(msgBytes)))
   356  	}
   357  	return res
   358  }
   359  
   360  // TrySendEnvelope attempts to sends the message in the envelope on the channel specified by the
   361  // envelope. Returns false immediately if the connection's internal queue is full
   362  // Using TrySendEnvelope allows for tracking the message bytes sent and received by message type
   363  // as a metric which TrySend cannot support.
   364  func (p *peer) TrySendEnvelope(e Envelope) bool {
   365  	if !p.IsRunning() {
   366  		// see Switch#Broadcast, where we fetch the list of peers and loop over
   367  		// them - while we're looping, one peer may be removed and stopped.
   368  		return false
   369  	} else if !p.hasChannel(e.ChannelID) {
   370  		return false
   371  	}
   372  	msg := e.Message
   373  	metricLabelValue := p.mlc.ValueToMetricLabel(msg)
   374  	if w, ok := msg.(Wrapper); ok {
   375  		msg = w.Wrap()
   376  	}
   377  	msgBytes, err := proto.Marshal(msg)
   378  	if err != nil {
   379  		p.Logger.Error("marshaling message to send", "error", err)
   380  		return false
   381  	}
   382  	res := p.TrySend(e.ChannelID, msgBytes)
   383  	if res {
   384  		p.metrics.MessageSendBytesTotal.With("message_type", metricLabelValue).Add(float64(len(msgBytes)))
   385  	}
   386  	return res
   387  }
   388  
   389  // TrySend msg bytes to the channel identified by chID byte. Immediately returns
   390  // false if the send queue is full.
   391  // TrySendEnvelope replaces TrySend which will be deprecated in a future release.
   392  func (p *peer) TrySend(chID byte, msgBytes []byte) bool {
   393  	if !p.IsRunning() {
   394  		return false
   395  	} else if !p.hasChannel(chID) {
   396  		return false
   397  	}
   398  	res := p.mconn.TrySend(chID, msgBytes)
   399  	if res {
   400  		labels := []string{
   401  			"peer_id", string(p.ID()),
   402  			"chID", fmt.Sprintf("%#x", chID),
   403  		}
   404  		p.metrics.PeerSendBytesTotal.With(labels...).Add(float64(len(msgBytes)))
   405  	}
   406  	return res
   407  }
   408  
   409  // Get the data for a given key.
   410  func (p *peer) Get(key string) interface{} {
   411  	return p.Data.Get(key)
   412  }
   413  
   414  // Set sets the data for the given key.
   415  func (p *peer) Set(key string, data interface{}) {
   416  	p.Data.Set(key, data)
   417  }
   418  
   419  // hasChannel returns true if the peer reported
   420  // knowing about the given chID.
   421  func (p *peer) hasChannel(chID byte) bool {
   422  	for _, ch := range p.channels {
   423  		if ch == chID {
   424  			return true
   425  		}
   426  	}
   427  	// NOTE: probably will want to remove this
   428  	// but could be helpful while the feature is new
   429  	p.Logger.Debug(
   430  		"Unknown channel for peer",
   431  		"channel",
   432  		chID,
   433  		"channels",
   434  		p.channels,
   435  	)
   436  	return false
   437  }
   438  
   439  // CloseConn closes original connection. Used for cleaning up in cases where the peer had not been started at all.
   440  func (p *peer) CloseConn() error {
   441  	return p.peerConn.conn.Close()
   442  }
   443  
   444  func (p *peer) SetRemovalFailed() {
   445  	p.removalAttemptFailed = true
   446  }
   447  
   448  func (p *peer) GetRemovalFailed() bool {
   449  	return p.removalAttemptFailed
   450  }
   451  
   452  //---------------------------------------------------
   453  // methods only used for testing
   454  // TODO: can we remove these?
   455  
   456  // CloseConn closes the underlying connection
   457  func (pc *peerConn) CloseConn() {
   458  	pc.conn.Close()
   459  }
   460  
   461  // RemoteAddr returns peer's remote network address.
   462  func (p *peer) RemoteAddr() net.Addr {
   463  	return p.peerConn.conn.RemoteAddr()
   464  }
   465  
   466  // CanSend returns true if the send queue is not full, false otherwise.
   467  func (p *peer) CanSend(chID byte) bool {
   468  	if !p.IsRunning() {
   469  		return false
   470  	}
   471  	return p.mconn.CanSend(chID)
   472  }
   473  
   474  //---------------------------------------------------
   475  
   476  func PeerMetrics(metrics *Metrics) PeerOption {
   477  	return func(p *peer) {
   478  		p.metrics = metrics
   479  	}
   480  }
   481  
   482  func (p *peer) metricsReporter() {
   483  	for {
   484  		select {
   485  		case <-p.metricsTicker.C:
   486  			status := p.mconn.Status()
   487  			var sendQueueSize float64
   488  			for _, chStatus := range status.Channels {
   489  				sendQueueSize += float64(chStatus.SendQueueSize)
   490  			}
   491  
   492  			p.metrics.PeerPendingSendBytes.With("peer_id", string(p.ID())).Set(sendQueueSize)
   493  		case <-p.Quit():
   494  			return
   495  		}
   496  	}
   497  }
   498  
   499  //------------------------------------------------------------------
   500  // helper funcs
   501  
   502  func createMConnection(
   503  	conn net.Conn,
   504  	p *peer,
   505  	reactorsByCh map[byte]Reactor,
   506  	msgTypeByChID map[byte]proto.Message,
   507  	chDescs []*tmconn.ChannelDescriptor,
   508  	onPeerError func(Peer, interface{}),
   509  	config tmconn.MConnConfig,
   510  ) *tmconn.MConnection {
   511  
   512  	onReceive := func(chID byte, msgBytes []byte) {
   513  		reactor := reactorsByCh[chID]
   514  		if reactor == nil {
   515  			// Note that its ok to panic here as it's caught in the conn._recover,
   516  			// which does onPeerError.
   517  			panic(fmt.Sprintf("Unknown channel %X", chID))
   518  		}
   519  		mt := msgTypeByChID[chID]
   520  		msg := proto.Clone(mt)
   521  		err := proto.Unmarshal(msgBytes, msg)
   522  		if err != nil {
   523  			panic(fmt.Errorf("unmarshaling message: %s into type: %s", err, reflect.TypeOf(mt)))
   524  		}
   525  		labels := []string{
   526  			"peer_id", string(p.ID()),
   527  			"chID", fmt.Sprintf("%#x", chID),
   528  		}
   529  		if w, ok := msg.(Unwrapper); ok {
   530  			msg, err = w.Unwrap()
   531  			if err != nil {
   532  				panic(fmt.Errorf("unwrapping message: %s", err))
   533  			}
   534  		}
   535  		p.metrics.PeerReceiveBytesTotal.With(labels...).Add(float64(len(msgBytes)))
   536  		p.metrics.MessageReceiveBytesTotal.With("message_type", p.mlc.ValueToMetricLabel(msg)).Add(float64(len(msgBytes)))
   537  		if nr, ok := reactor.(EnvelopeReceiver); ok {
   538  			nr.ReceiveEnvelope(Envelope{
   539  				ChannelID: chID,
   540  				Src:       p,
   541  				Message:   msg,
   542  			})
   543  		} else {
   544  			reactor.Receive(chID, p, msgBytes)
   545  		}
   546  	}
   547  
   548  	onError := func(r interface{}) {
   549  		onPeerError(p, r)
   550  	}
   551  
   552  	return tmconn.NewMConnectionWithConfig(
   553  		conn,
   554  		chDescs,
   555  		onReceive,
   556  		onError,
   557  		config,
   558  	)
   559  }