github.com/DFWallet/tendermint-cosmos@v0.0.2/p2p/peer.go (about)

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