github.com/koko1123/flow-go-1@v0.29.6/network/p2p/p2pnode/libp2pNode.go (about) 1 // Package p2pnode encapsulates the libp2p library 2 package p2pnode 3 4 import ( 5 "context" 6 "errors" 7 "fmt" 8 "sync" 9 "time" 10 11 "github.com/koko1123/flow-go-1/module/irrecoverable" 12 13 "github.com/hashicorp/go-multierror" 14 dht "github.com/libp2p/go-libp2p-kad-dht" 15 kbucket "github.com/libp2p/go-libp2p-kbucket" 16 "github.com/libp2p/go-libp2p/core/host" 17 libp2pnet "github.com/libp2p/go-libp2p/core/network" 18 "github.com/libp2p/go-libp2p/core/peer" 19 "github.com/libp2p/go-libp2p/core/protocol" 20 "github.com/libp2p/go-libp2p/core/routing" 21 "github.com/rs/zerolog" 22 23 "github.com/koko1123/flow-go-1/module/component" 24 flownet "github.com/koko1123/flow-go-1/network" 25 "github.com/koko1123/flow-go-1/network/channels" 26 "github.com/koko1123/flow-go-1/network/internal/p2putils" 27 "github.com/koko1123/flow-go-1/network/p2p" 28 "github.com/koko1123/flow-go-1/network/p2p/connection" 29 "github.com/koko1123/flow-go-1/network/p2p/unicast" 30 ) 31 32 const ( 33 _ = iota 34 _ = 1 << (10 * iota) 35 mb 36 ) 37 38 const ( 39 // MaxConnectAttempt is the maximum number of attempts to be made to connect to a remote node for 1-1 direct communication 40 MaxConnectAttempt = 3 41 42 // DefaultMaxPubSubMsgSize defines the maximum message size in publish and multicast modes 43 DefaultMaxPubSubMsgSize = 5 * mb // 5 mb 44 45 // timeout for FindPeer queries to the routing system 46 // TODO: is this a sensible value? 47 findPeerQueryTimeout = 10 * time.Second 48 ) 49 50 // Node is a wrapper around the LibP2P host. 51 type Node struct { 52 component.Component 53 sync.RWMutex 54 uniMgr *unicast.Manager 55 host host.Host // reference to the libp2p host (https://godoc.org/github.com/libp2p/go-libp2p/core/host) 56 pubSub p2p.PubSubAdapter 57 logger zerolog.Logger // used to provide logging 58 topics map[channels.Topic]p2p.Topic // map of a topic string to an actual topic instance 59 subs map[channels.Topic]p2p.Subscription // map of a topic string to an actual subscription 60 routing routing.Routing 61 pCache *ProtocolPeerCache 62 peerManager *connection.PeerManager 63 } 64 65 // NewNode creates a new libp2p node and sets its parameters. 66 func NewNode( 67 logger zerolog.Logger, 68 host host.Host, 69 pCache *ProtocolPeerCache, 70 uniMgr *unicast.Manager, 71 peerManager *connection.PeerManager, 72 ) *Node { 73 return &Node{ 74 uniMgr: uniMgr, 75 host: host, 76 logger: logger.With().Str("component", "libp2p-node").Logger(), 77 topics: make(map[channels.Topic]p2p.Topic), 78 subs: make(map[channels.Topic]p2p.Subscription), 79 pCache: pCache, 80 peerManager: peerManager, 81 } 82 } 83 84 var _ component.Component = (*Node)(nil) 85 86 func (n *Node) Start(ctx irrecoverable.SignalerContext) { 87 n.Component.Start(ctx) 88 } 89 90 // Stop terminates the libp2p node. 91 // All errors returned from this function can be considered benign. 92 func (n *Node) Stop() error { 93 var result error 94 95 n.logger.Debug().Msg("unsubscribing from all topics") 96 for t := range n.topics { 97 err := n.UnSubscribe(t) 98 // context cancelled errors are expected while unsubscribing from topics during shutdown 99 if err != nil && !errors.Is(err, context.Canceled) { 100 result = multierror.Append(result, err) 101 } 102 } 103 104 n.logger.Debug().Msg("stopping libp2p node") 105 if err := n.host.Close(); err != nil { 106 result = multierror.Append(result, err) 107 } 108 109 n.logger.Debug().Msg("closing peer store") 110 // to prevent peerstore routine leak (https://github.com/libp2p/go-libp2p/issues/718) 111 if err := n.host.Peerstore().Close(); err != nil { 112 n.logger.Debug().Err(err).Msg("closing peer store") 113 result = multierror.Append(result, err) 114 } 115 116 if result != nil { 117 return result 118 } 119 120 addrs := len(n.host.Network().ListenAddresses()) 121 ticker := time.NewTicker(time.Millisecond * 2) 122 defer ticker.Stop() 123 timeout := time.After(time.Second) 124 for addrs > 0 { 125 // wait for all listen addresses to have been removed 126 select { 127 case <-timeout: 128 n.logger.Error().Int("port", addrs).Msg("listen addresses still open") 129 return nil 130 case <-ticker.C: 131 addrs = len(n.host.Network().ListenAddresses()) 132 } 133 } 134 135 n.logger.Debug().Msg("libp2p node stopped successfully") 136 137 return nil 138 } 139 140 // AddPeer adds a peer to this node by adding it to this node's peerstore and connecting to it. 141 // All errors returned from this function can be considered benign. 142 func (n *Node) AddPeer(ctx context.Context, peerInfo peer.AddrInfo) error { 143 return n.host.Connect(ctx, peerInfo) 144 } 145 146 // RemovePeer closes the connection with the peer. 147 // All errors returned from this function can be considered benign. 148 func (n *Node) RemovePeer(peerID peer.ID) error { 149 err := n.host.Network().ClosePeer(peerID) 150 if err != nil { 151 return fmt.Errorf("failed to remove peer %s: %w", peerID, err) 152 } 153 return nil 154 } 155 156 // GetPeersForProtocol returns slice peer IDs for the specified protocol ID. 157 func (n *Node) GetPeersForProtocol(pid protocol.ID) peer.IDSlice { 158 pMap := n.pCache.GetPeers(pid) 159 peers := make(peer.IDSlice, 0, len(pMap)) 160 for p := range pMap { 161 peers = append(peers, p) 162 } 163 return peers 164 } 165 166 // CreateStream returns an existing stream connected to the peer if it exists, or creates a new stream with it. 167 // All errors returned from this function can be considered benign. 168 func (n *Node) CreateStream(ctx context.Context, peerID peer.ID) (libp2pnet.Stream, error) { 169 lg := n.logger.With().Str("peer_id", peerID.String()).Logger() 170 171 // If we do not currently have any addresses for the given peer, stream creation will almost 172 // certainly fail. If this Node was configured with a routing system, we can try to use it to 173 // look up the address of the peer. 174 if len(n.host.Peerstore().Addrs(peerID)) == 0 && n.routing != nil { 175 lg.Info().Msg("address not found in peer store, searching for peer in routing system") 176 177 var err error 178 func() { 179 timedCtx, cancel := context.WithTimeout(ctx, findPeerQueryTimeout) 180 defer cancel() 181 // try to find the peer using the routing system 182 _, err = n.routing.FindPeer(timedCtx, peerID) 183 }() 184 185 if err != nil { 186 lg.Warn().Err(err).Msg("address not found in both peer store and routing system") 187 } else { 188 lg.Debug().Msg("address not found in peer store, but found in routing system search") 189 } 190 } 191 stream, dialAddrs, err := n.uniMgr.CreateStream(ctx, peerID, MaxConnectAttempt) 192 if err != nil { 193 return nil, flownet.NewPeerUnreachableError(fmt.Errorf("could not create stream (peer_id: %s, dialing address(s): %v): %w", peerID, 194 dialAddrs, err)) 195 } 196 197 lg.Info(). 198 Str("networking_protocol_id", string(stream.Protocol())). 199 Str("dial_address", fmt.Sprintf("%v", dialAddrs)). 200 Msg("stream successfully created to remote peer") 201 return stream, nil 202 } 203 204 // GetIPPort returns the IP and Port the libp2p node is listening on. 205 // All errors returned from this function can be considered benign. 206 func (n *Node) GetIPPort() (string, string, error) { 207 return p2putils.IPPortFromMultiAddress(n.host.Network().ListenAddresses()...) 208 } 209 210 // RoutingTable returns the node routing table 211 func (n *Node) RoutingTable() *kbucket.RoutingTable { 212 return n.routing.(*dht.IpfsDHT).RoutingTable() 213 } 214 215 // ListPeers returns list of peer IDs for peers subscribed to the topic. 216 func (n *Node) ListPeers(topic string) []peer.ID { 217 return n.pubSub.ListPeers(topic) 218 } 219 220 // Subscribe subscribes the node to the given topic and returns the subscription 221 // All errors returned from this function can be considered benign. 222 func (n *Node) Subscribe(topic channels.Topic, topicValidator p2p.TopicValidatorFunc) (p2p.Subscription, error) { 223 n.Lock() 224 defer n.Unlock() 225 226 // Check if the topic has been already created and is in the cache 227 n.pubSub.GetTopics() 228 tp, found := n.topics[topic] 229 var err error 230 if !found { 231 if err := n.pubSub.RegisterTopicValidator(topic.String(), topicValidator); err != nil { 232 n.logger.Err(err).Str("topic", topic.String()).Msg("failed to register topic validator, aborting subscription") 233 return nil, fmt.Errorf("failed to register topic validator: %w", err) 234 } 235 236 tp, err = n.pubSub.Join(topic.String()) 237 if err != nil { 238 if err := n.pubSub.UnregisterTopicValidator(topic.String()); err != nil { 239 n.logger.Err(err).Str("topic", topic.String()).Msg("failed to unregister topic validator") 240 } 241 242 return nil, fmt.Errorf("could not join topic (%s): %w", topic, err) 243 } 244 245 n.topics[topic] = tp 246 } 247 248 // Create a new subscription 249 s, err := tp.Subscribe() 250 if err != nil { 251 return s, fmt.Errorf("could not subscribe to topic (%s): %w", topic, err) 252 } 253 254 // Add the subscription to the cache 255 n.subs[topic] = s 256 257 n.logger.Debug(). 258 Str("topic", topic.String()). 259 Msg("subscribed to topic") 260 return s, err 261 } 262 263 // UnSubscribe cancels the subscriber and closes the topic. 264 // All errors returned from this function can be considered benign. 265 func (n *Node) UnSubscribe(topic channels.Topic) error { 266 n.Lock() 267 defer n.Unlock() 268 // Remove the Subscriber from the cache 269 if s, found := n.subs[topic]; found { 270 s.Cancel() 271 n.subs[topic] = nil 272 delete(n.subs, topic) 273 } 274 275 tp, found := n.topics[topic] 276 if !found { 277 err := fmt.Errorf("could not find topic (%s)", topic) 278 return err 279 } 280 281 if err := n.pubSub.UnregisterTopicValidator(topic.String()); err != nil { 282 n.logger.Err(err).Str("topic", topic.String()).Msg("failed to unregister topic validator") 283 } 284 285 // attempt to close the topic 286 err := tp.Close() 287 if err != nil { 288 err = fmt.Errorf("could not close topic (%s): %w", topic, err) 289 return err 290 } 291 n.topics[topic] = nil 292 delete(n.topics, topic) 293 294 n.logger.Debug(). 295 Str("topic", topic.String()). 296 Msg("unsubscribed from topic") 297 return err 298 } 299 300 // Publish publishes the given payload on the topic. 301 // All errors returned from this function can be considered benign. 302 func (n *Node) Publish(ctx context.Context, topic channels.Topic, data []byte) error { 303 ps, found := n.topics[topic] 304 if !found { 305 return fmt.Errorf("could not find topic (%s)", topic) 306 } 307 err := ps.Publish(ctx, data) 308 if err != nil { 309 return fmt.Errorf("could not publish to topic (%s): %w", topic, err) 310 } 311 return nil 312 } 313 314 // HasSubscription returns true if the node currently has an active subscription to the topic. 315 func (n *Node) HasSubscription(topic channels.Topic) bool { 316 n.RLock() 317 defer n.RUnlock() 318 _, ok := n.subs[topic] 319 return ok 320 } 321 322 // Host returns pointer to host object of node. 323 func (n *Node) Host() host.Host { 324 return n.host 325 } 326 327 // WithDefaultUnicastProtocol overrides the default handler of the unicast manager and registers all preferred protocols. 328 func (n *Node) WithDefaultUnicastProtocol(defaultHandler libp2pnet.StreamHandler, preferred []unicast.ProtocolName) error { 329 n.uniMgr.WithDefaultHandler(defaultHandler) 330 for _, p := range preferred { 331 err := n.uniMgr.Register(p) 332 if err != nil { 333 return fmt.Errorf("could not register unicast protocls: %w", err) 334 } 335 } 336 337 return nil 338 } 339 340 // WithPeersProvider sets the PeersProvider for the peer manager. 341 // If a peer manager factory is set, this method will set the peer manager's PeersProvider. 342 func (n *Node) WithPeersProvider(peersProvider p2p.PeersProvider) { 343 if n.peerManager != nil { 344 n.peerManager.SetPeersProvider(peersProvider) 345 } 346 } 347 348 // PeerManagerComponent returns the component interface of the peer manager. 349 func (n *Node) PeerManagerComponent() component.Component { 350 return n.peerManager 351 } 352 353 // RequestPeerUpdate requests an update to the peer connections of this node using the peer manager. 354 func (n *Node) RequestPeerUpdate() { 355 if n.peerManager != nil { 356 n.peerManager.RequestPeerUpdate() 357 } 358 } 359 360 // IsConnected returns true is address is a direct peer of this node else false 361 func (n *Node) IsConnected(peerID peer.ID) (bool, error) { 362 isConnected := n.host.Network().Connectedness(peerID) == libp2pnet.Connected 363 return isConnected, nil 364 } 365 366 // SetRouting sets the node's routing implementation. 367 // SetRouting may be called at most once. 368 func (n *Node) SetRouting(r routing.Routing) { 369 if n.routing != nil { 370 n.logger.Fatal().Msg("routing already set") 371 } 372 373 n.routing = r 374 } 375 376 // Routing returns the node's routing implementation. 377 func (n *Node) Routing() routing.Routing { 378 return n.routing 379 } 380 381 // SetPubSub sets the node's pubsub implementation. 382 // SetPubSub may be called at most once. 383 func (n *Node) SetPubSub(ps p2p.PubSubAdapter) { 384 if n.pubSub != nil { 385 n.logger.Fatal().Msg("pubSub already set") 386 } 387 388 n.pubSub = ps 389 } 390 391 // SetComponentManager sets the component manager for the node. 392 // SetComponentManager may be called at most once. 393 func (n *Node) SetComponentManager(cm *component.ComponentManager) { 394 if n.Component != nil { 395 n.logger.Fatal().Msg("component already set") 396 } 397 398 n.Component = cm 399 }