github.com/onflow/flow-go@v0.35.7-crescendo-preview.23-atree-inlining/network/internal/p2putils/utils.go (about)

     1  package p2putils
     2  
     3  import (
     4  	"fmt"
     5  	"net"
     6  
     7  	"github.com/libp2p/go-libp2p/core/crypto"
     8  	"github.com/libp2p/go-libp2p/core/host"
     9  	"github.com/libp2p/go-libp2p/core/network"
    10  	"github.com/libp2p/go-libp2p/core/peer"
    11  	"github.com/libp2p/go-libp2p/core/protocol"
    12  	"github.com/multiformats/go-multiaddr"
    13  	"github.com/rs/zerolog"
    14  
    15  	"github.com/onflow/flow-go/model/flow"
    16  	"github.com/onflow/flow-go/network/p2p/keyutils"
    17  	p2plogging "github.com/onflow/flow-go/network/p2p/logging"
    18  	"github.com/onflow/flow-go/network/p2p/unicast/protocols"
    19  )
    20  
    21  // FlowStream returns the Flow protocol Stream in the connection if one exist, else it returns nil
    22  func FlowStream(conn network.Conn) network.Stream {
    23  	for _, s := range conn.GetStreams() {
    24  		if protocols.IsFlowProtocolStream(s) {
    25  			return s
    26  		}
    27  	}
    28  	return nil
    29  }
    30  
    31  // StreamLogger creates a logger for libp2p stream which logs the remote and local peer IDs and addresses
    32  func StreamLogger(log zerolog.Logger, stream network.Stream) zerolog.Logger {
    33  	logger := log.With().
    34  		Str("protocol", string(stream.Protocol())).
    35  		Str("remote_peer", p2plogging.PeerId(stream.Conn().RemotePeer())).
    36  		Str("remote_address", stream.Conn().RemoteMultiaddr().String()).
    37  		Str("local_peer", p2plogging.PeerId(stream.Conn().LocalPeer())).
    38  		Str("local_address", stream.Conn().LocalMultiaddr().String()).Logger()
    39  	return logger
    40  }
    41  
    42  var directionLookUp = map[network.Direction]string{
    43  	network.DirInbound:  "InBound",
    44  	network.DirOutbound: "OutBound",
    45  	network.DirUnknown:  "Unknown",
    46  }
    47  
    48  var connectednessLookup = map[network.Connectedness]string{
    49  	network.CannotConnect: "CannotConnect",
    50  	network.CanConnect:    "CanConnect",
    51  	network.Connected:     "Connected",
    52  	network.NotConnected:  "NotConnected",
    53  }
    54  
    55  // DirectionToString reverse translates libp2p network direction to string
    56  func DirectionToString(direction network.Direction) (string, bool) {
    57  	if dirStr, found := directionLookUp[direction]; found {
    58  		return dirStr, true
    59  	}
    60  	return "", false
    61  }
    62  
    63  // ConnectednessToString reverse translates libp2p network connectedness to string
    64  func ConnectednessToString(connectedness network.Connectedness) (string, bool) {
    65  	if connStr, found := connectednessLookup[connectedness]; found {
    66  		return connStr, true
    67  	}
    68  	return "", false
    69  
    70  }
    71  
    72  // CountStream finds total number of outbound stream to the target id
    73  func CountStream(host host.Host, targetID peer.ID, opts ...FilterOption) int {
    74  	streams := FilterStream(host, targetID, append(opts, All())...)
    75  	return len(streams)
    76  }
    77  
    78  // FilterOptions holds the filtering options used in FilterStream.
    79  type FilterOptions struct {
    80  	// dir specifies the direction of the streams to be filtered.
    81  	// The default value is network.DirBoth, which considers both inbound and outbound streams.
    82  	dir network.Direction
    83  
    84  	// protocol specifies the protocol ID of the streams to be filtered.
    85  	// The default value is an empty string, which considers streams of all protocol IDs.
    86  	protocol protocol.ID
    87  
    88  	// all specifies whether to return all matching streams or just the first matching stream.
    89  	// The default value is false, which returns just the first matching stream.
    90  	all bool
    91  }
    92  
    93  // FilterOption defines a function type that modifies FilterOptions.
    94  type FilterOption func(*FilterOptions)
    95  
    96  // Direction is a FilterOption for setting the direction of the streams to be filtered.
    97  func Direction(dir network.Direction) FilterOption {
    98  	return func(opts *FilterOptions) {
    99  		opts.dir = dir
   100  	}
   101  }
   102  
   103  // Protocol is a FilterOption for setting the protocol ID of the streams to be filtered.
   104  func Protocol(protocol protocol.ID) FilterOption {
   105  	return func(opts *FilterOptions) {
   106  		opts.protocol = protocol
   107  	}
   108  }
   109  
   110  // All is a FilterOption for setting whether to return all matching streams or just the first matching stream.
   111  func All() FilterOption {
   112  	return func(opts *FilterOptions) {
   113  		opts.all = true
   114  	}
   115  }
   116  
   117  // FilterStream filters the streams to a target peer based on the provided options.
   118  // The default behavior is to consider all directions and protocols, and return just the first matching stream.
   119  // This behavior can be customized by providing FilterOption values.
   120  //
   121  // Usage:
   122  //
   123  //   - To find all outbound streams to a target peer with a specific protocol ID:
   124  //     streams := FilterStream(host, targetID, Direction(network.DirOutbound), Protocol(myProtocolID), All(true))
   125  //
   126  //   - To find the first inbound stream to a target peer, regardless of protocol ID:
   127  //     stream := FilterStream(host, targetID, Direction(network.DirInbound))
   128  //
   129  // host is the host from which to filter streams.
   130  // targetID is the ID of the target peer.
   131  // options is a variadic parameter that allows zero or more FilterOption values to be provided.
   132  //
   133  // It returns a slice of network.Stream values that match the filtering criteria.
   134  func FilterStream(host host.Host, targetID peer.ID, options ...FilterOption) []network.Stream {
   135  	var filteredStreams []network.Stream
   136  	const allProtocols = "*"
   137  	// default values
   138  	opts := FilterOptions{
   139  		dir:      network.DirUnknown, // by default, consider both inbound and outbound streams
   140  		protocol: allProtocols,       // by default, consider streams of all protocol IDs
   141  		all:      false,              // by default, return just the first matching stream
   142  	}
   143  
   144  	// apply provided options
   145  	for _, option := range options {
   146  		option(&opts)
   147  	}
   148  
   149  	if host.Network().Connectedness(targetID) != network.Connected {
   150  		return filteredStreams
   151  	}
   152  
   153  	conns := host.Network().ConnsToPeer(targetID)
   154  	for _, conn := range conns {
   155  		streams := conn.GetStreams()
   156  		for _, stream := range streams {
   157  			if (opts.dir == network.DirUnknown || stream.Stat().Direction == opts.dir) &&
   158  				(opts.protocol == allProtocols || stream.Protocol() == opts.protocol) {
   159  				filteredStreams = append(filteredStreams, stream)
   160  				if !opts.all {
   161  					return filteredStreams
   162  				}
   163  			}
   164  		}
   165  	}
   166  	return filteredStreams
   167  }
   168  
   169  // NetworkingInfo returns ip, port, libp2p public key of the identity.
   170  func NetworkingInfo(identity flow.IdentitySkeleton) (string, string, crypto.PubKey, error) {
   171  	// split the node address into ip and port
   172  	ip, port, err := net.SplitHostPort(identity.Address)
   173  	if err != nil {
   174  		return "", "", nil, fmt.Errorf("could not parse address %s: %w", identity.Address, err)
   175  	}
   176  
   177  	// convert the Flow key to a LibP2P key
   178  	lkey, err := keyutils.LibP2PPublicKeyFromFlow(identity.NetworkPubKey)
   179  	if err != nil {
   180  		return "", "", nil, fmt.Errorf("could not convert flow key to libp2p key: %w", err)
   181  	}
   182  
   183  	return ip, port, lkey, nil
   184  }
   185  
   186  // IPPortFromMultiAddress returns the IP/hostname and the port for the given multi-addresses
   187  // associated with a libp2p host
   188  func IPPortFromMultiAddress(addrs ...multiaddr.Multiaddr) (string, string, error) {
   189  
   190  	var ipOrHostname, port string
   191  	var err error
   192  
   193  	for _, a := range addrs {
   194  		// try and get the dns4 hostname
   195  		ipOrHostname, err = a.ValueForProtocol(multiaddr.P_DNS4)
   196  		if err != nil {
   197  			// if dns4 hostname is not found, try and get the IP address
   198  			ipOrHostname, err = a.ValueForProtocol(multiaddr.P_IP4)
   199  			if err != nil {
   200  				continue // this may not be a TCP IP multiaddress
   201  			}
   202  		}
   203  
   204  		// if either IP address or hostname is found, look for the port number
   205  		port, err = a.ValueForProtocol(multiaddr.P_TCP)
   206  		if err != nil {
   207  			// an IPv4 or DNS4 based multiaddress should have a port number
   208  			return "", "", err
   209  		}
   210  
   211  		// there should only be one valid IPv4 address
   212  		return ipOrHostname, port, nil
   213  	}
   214  	return "", "", fmt.Errorf("ip address or hostname not found")
   215  }