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  }