github.com/onflow/flow-go@v0.35.7-crescendo-preview.23-atree-inlining/network/p2p/unicast/stream/factory.go (about) 1 package stream 2 3 import ( 4 "context" 5 "errors" 6 "fmt" 7 "strings" 8 9 "github.com/libp2p/go-libp2p/core/host" 10 "github.com/libp2p/go-libp2p/core/network" 11 "github.com/libp2p/go-libp2p/core/peer" 12 "github.com/libp2p/go-libp2p/core/protocol" 13 "github.com/libp2p/go-libp2p/p2p/net/swarm" 14 15 "github.com/onflow/flow-go/network/p2p" 16 ) 17 18 const ( 19 protocolNegotiationFailedStr = "failed to negotiate security protocol" 20 protocolNotSupportedStr = "protocol not supported" 21 ) 22 23 type LibP2PStreamFactory struct { 24 host host.Host 25 } 26 27 var _ p2p.StreamFactory = (*LibP2PStreamFactory)(nil) 28 29 func NewLibP2PStreamFactory(h host.Host) p2p.StreamFactory { 30 return &LibP2PStreamFactory{host: h} 31 } 32 33 func (l *LibP2PStreamFactory) SetStreamHandler(pid protocol.ID, handler network.StreamHandler) { 34 l.host.SetStreamHandler(pid, handler) 35 } 36 37 // NewStream establishes a new stream with the given peer using the provided protocol.ID on the libp2p host. 38 // This function is a critical part of the network communication, facilitating the creation of a dedicated 39 // bidirectional channel (stream) between two nodes in the network. 40 // If there exists no connection between the two nodes, the function attempts to establish one before creating the stream. 41 // If there are multiple connections between the two nodes, the function selects the best one (based on libp2p internal criteria) to create the stream. 42 // 43 // Usage: 44 // The function is intended to be used when there is a need to initiate a direct communication stream with a peer. 45 // It is typically invoked in scenarios where a node wants to send a message or start a series of messages to another 46 // node using a specific protocol. The protocol ID is used to ensure that both nodes communicate over the same 47 // protocol, which defines the structure and semantics of the communication. 48 // 49 // Expected errors: 50 // During normal operation, the function may encounter specific expected errors, which are handled as follows: 51 // 52 // - ErrProtocolNotSupported: This error occurs when the remote node does not support the specified protocol ID, 53 // which may indicate that the remote node is running a different version of the software or a different spork. 54 // The error contains details about the peer ID and the unsupported protocol, and it is generated when the 55 // underlying error message indicates a protocol mismatch. This is a critical error as it signifies that the 56 // two nodes cannot communicate using the requested protocol, and it must be handled by either retrying with 57 // a different protocol ID or by performing some form of negotiation or fallback. 58 // 59 // - ErrSecurityProtocolNegotiationFailed this indicates there was an issue upgrading the connection. 60 // 61 // - ErrGaterDisallowedConnection this indicates the connection was disallowed by the gater. 62 // 63 // - Any other error returned by the libp2p host: This error indicates that the stream creation failed due to 64 // some unexpected error, which may be caused by a variety of reasons. This is NOT a critical error, and it 65 // can be handled by retrying the stream creation or by performing some other action. Crashing node upon this 66 // error is NOT recommended. 67 // 68 // Arguments: 69 // - ctx: A context.Context that governs the lifetime of the stream creation. It can be used to cancel the 70 // operation or to set deadlines. 71 // - p: The peer.ID of the target node with which the stream is to be established. 72 // - pid: The protocol.ID that specifies the communication protocol to be used for the stream. 73 // 74 // Returns: 75 // - network.Stream: The successfully created stream, ready for reading and writing, or nil if an error occurs. 76 // - error: An error encountered during stream creation, wrapped in a contextually appropriate error type when necessary, 77 // or nil if the operation is successful. 78 func (l *LibP2PStreamFactory) NewStream(ctx context.Context, p peer.ID, pid protocol.ID) (network.Stream, error) { 79 s, err := l.host.NewStream(ctx, p, pid) 80 switch { 81 case err == nil: 82 return s, nil 83 case strings.Contains(err.Error(), protocolNotSupportedStr): 84 return nil, NewProtocolNotSupportedErr(p, pid, err) 85 case strings.Contains(err.Error(), protocolNegotiationFailedStr): 86 return nil, NewSecurityProtocolNegotiationErr(p, err) 87 case errors.Is(err, swarm.ErrGaterDisallowedConnection): 88 return nil, NewGaterDisallowedConnectionErr(err) 89 default: 90 return nil, fmt.Errorf("failed to create stream: %w", err) 91 } 92 }