github.com/gnolang/gno@v0.0.0-20240520182011-228e9d0192ce/tm2/pkg/p2p/peer.go (about)

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