github.com/evdatsion/aphelion-dpos-bft@v0.32.1/p2p/peer.go (about)

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