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