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 }