github.com/ethersphere/bee/v2@v2.2.0/pkg/p2p/libp2p/libp2p.go (about)

     1  // Copyright 2020 The Swarm Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  package libp2p
     6  
     7  import (
     8  	"context"
     9  	"crypto/ecdsa"
    10  	"errors"
    11  	"fmt"
    12  	"net"
    13  	"os"
    14  	"runtime"
    15  	"strconv"
    16  	"strings"
    17  	"sync"
    18  	"time"
    19  
    20  	"github.com/ethersphere/bee/v2"
    21  	"github.com/ethersphere/bee/v2/pkg/addressbook"
    22  	"github.com/ethersphere/bee/v2/pkg/bzz"
    23  	beecrypto "github.com/ethersphere/bee/v2/pkg/crypto"
    24  	"github.com/ethersphere/bee/v2/pkg/log"
    25  	"github.com/ethersphere/bee/v2/pkg/p2p"
    26  	"github.com/ethersphere/bee/v2/pkg/p2p/libp2p/internal/blocklist"
    27  	"github.com/ethersphere/bee/v2/pkg/p2p/libp2p/internal/breaker"
    28  	"github.com/ethersphere/bee/v2/pkg/p2p/libp2p/internal/handshake"
    29  	"github.com/ethersphere/bee/v2/pkg/p2p/libp2p/internal/reacher"
    30  	"github.com/ethersphere/bee/v2/pkg/storage"
    31  	"github.com/ethersphere/bee/v2/pkg/swarm"
    32  	"github.com/ethersphere/bee/v2/pkg/topology"
    33  	"github.com/ethersphere/bee/v2/pkg/topology/lightnode"
    34  	"github.com/ethersphere/bee/v2/pkg/tracing"
    35  	"github.com/libp2p/go-libp2p"
    36  	"github.com/libp2p/go-libp2p/core/crypto"
    37  	"github.com/libp2p/go-libp2p/core/event"
    38  	"github.com/libp2p/go-libp2p/core/host"
    39  	"github.com/libp2p/go-libp2p/core/network"
    40  	libp2ppeer "github.com/libp2p/go-libp2p/core/peer"
    41  	"github.com/libp2p/go-libp2p/core/peerstore"
    42  	"github.com/libp2p/go-libp2p/core/protocol"
    43  	"github.com/libp2p/go-libp2p/p2p/host/autonat"
    44  	basichost "github.com/libp2p/go-libp2p/p2p/host/basic"
    45  	"github.com/libp2p/go-libp2p/p2p/host/peerstore/pstoremem"
    46  	rcmgr "github.com/libp2p/go-libp2p/p2p/host/resource-manager"
    47  	lp2pswarm "github.com/libp2p/go-libp2p/p2p/net/swarm"
    48  	libp2pping "github.com/libp2p/go-libp2p/p2p/protocol/ping"
    49  	"github.com/libp2p/go-libp2p/p2p/transport/tcp"
    50  	ws "github.com/libp2p/go-libp2p/p2p/transport/websocket"
    51  
    52  	ma "github.com/multiformats/go-multiaddr"
    53  	"github.com/multiformats/go-multistream"
    54  	"go.uber.org/atomic"
    55  
    56  	ocprom "contrib.go.opencensus.io/exporter/prometheus"
    57  	m2 "github.com/ethersphere/bee/v2/pkg/metrics"
    58  	rcmgrObs "github.com/libp2p/go-libp2p/p2p/host/resource-manager"
    59  	"github.com/prometheus/client_golang/prometheus"
    60  )
    61  
    62  // loggerName is the tree path name of the logger for this package.
    63  const loggerName = "libp2p"
    64  
    65  var (
    66  	_ p2p.Service      = (*Service)(nil)
    67  	_ p2p.DebugService = (*Service)(nil)
    68  
    69  	// reachabilityOverridePublic overrides autonat to simply report
    70  	// public reachability status, it is set in the makefile.
    71  	reachabilityOverridePublic = "false"
    72  )
    73  
    74  const (
    75  	defaultLightNodeLimit = 100
    76  	peerUserAgentTimeout  = time.Second
    77  
    78  	defaultHeadersRWTimeout = 10 * time.Second
    79  
    80  	IncomingStreamCountLimit = 5_000
    81  	OutgoingStreamCountLimit = 10_000
    82  )
    83  
    84  type Service struct {
    85  	ctx               context.Context
    86  	host              host.Host
    87  	natManager        basichost.NATManager
    88  	natAddrResolver   *staticAddressResolver
    89  	autonatDialer     host.Host
    90  	pingDialer        host.Host
    91  	libp2pPeerstore   peerstore.Peerstore
    92  	metrics           metrics
    93  	networkID         uint64
    94  	handshakeService  *handshake.Service
    95  	addressbook       addressbook.Putter
    96  	peers             *peerRegistry
    97  	connectionBreaker breaker.Interface
    98  	blocklist         *blocklist.Blocklist
    99  	protocols         []p2p.ProtocolSpec
   100  	notifier          p2p.PickyNotifier
   101  	logger            log.Logger
   102  	tracer            *tracing.Tracer
   103  	ready             chan struct{}
   104  	halt              chan struct{}
   105  	lightNodes        lightnodes
   106  	lightNodeLimit    int
   107  	protocolsmu       sync.RWMutex
   108  	reacher           p2p.Reacher
   109  	networkStatus     atomic.Int32
   110  	HeadersRWTimeout  time.Duration
   111  	autoNAT           autonat.AutoNAT
   112  }
   113  
   114  type lightnodes interface {
   115  	Connected(context.Context, p2p.Peer)
   116  	Disconnected(p2p.Peer)
   117  	Count() int
   118  	RandomPeer(swarm.Address) (swarm.Address, error)
   119  	EachPeer(pf topology.EachPeerFunc) error
   120  }
   121  
   122  type Options struct {
   123  	PrivateKey       *ecdsa.PrivateKey
   124  	NATAddr          string
   125  	EnableWS         bool
   126  	FullNode         bool
   127  	LightNodeLimit   int
   128  	WelcomeMessage   string
   129  	Nonce            []byte
   130  	ValidateOverlay  bool
   131  	hostFactory      func(...libp2p.Option) (host.Host, error)
   132  	HeadersRWTimeout time.Duration
   133  	Registry         *prometheus.Registry
   134  }
   135  
   136  func New(ctx context.Context, signer beecrypto.Signer, networkID uint64, overlay swarm.Address, addr string, ab addressbook.Putter, storer storage.StateStorer, lightNodes *lightnode.Container, logger log.Logger, tracer *tracing.Tracer, o Options) (*Service, error) {
   137  	host, port, err := net.SplitHostPort(addr)
   138  	if err != nil {
   139  		return nil, fmt.Errorf("address: %w", err)
   140  	}
   141  
   142  	ip4Addr := "0.0.0.0"
   143  	ip6Addr := "::"
   144  
   145  	if host != "" {
   146  		ip := net.ParseIP(host)
   147  		if ip4 := ip.To4(); ip4 != nil {
   148  			ip4Addr = ip4.String()
   149  			ip6Addr = ""
   150  		} else if ip6 := ip.To16(); ip6 != nil {
   151  			ip6Addr = ip6.String()
   152  			ip4Addr = ""
   153  		}
   154  	}
   155  
   156  	var listenAddrs []string
   157  	if ip4Addr != "" {
   158  		listenAddrs = append(listenAddrs, fmt.Sprintf("/ip4/%s/tcp/%s", ip4Addr, port))
   159  		if o.EnableWS {
   160  			listenAddrs = append(listenAddrs, fmt.Sprintf("/ip4/%s/tcp/%s/ws", ip4Addr, port))
   161  		}
   162  	}
   163  
   164  	if ip6Addr != "" {
   165  		listenAddrs = append(listenAddrs, fmt.Sprintf("/ip6/%s/tcp/%s", ip6Addr, port))
   166  		if o.EnableWS {
   167  			listenAddrs = append(listenAddrs, fmt.Sprintf("/ip6/%s/tcp/%s/ws", ip6Addr, port))
   168  		}
   169  	}
   170  
   171  	security := libp2p.DefaultSecurity
   172  	libp2pPeerstore, err := pstoremem.NewPeerstore()
   173  	if err != nil {
   174  		return nil, err
   175  	}
   176  
   177  	if o.Registry != nil {
   178  		rcmgrObs.MustRegisterWith(o.Registry)
   179  	}
   180  
   181  	_, err = ocprom.NewExporter(ocprom.Options{
   182  		Namespace: m2.Namespace,
   183  		Registry:  o.Registry,
   184  	})
   185  	if err != nil {
   186  		return nil, err
   187  	}
   188  
   189  	// Tweak certain settings
   190  	cfg := rcmgr.PartialLimitConfig{
   191  		System: rcmgr.ResourceLimits{
   192  			Streams:         IncomingStreamCountLimit + OutgoingStreamCountLimit,
   193  			StreamsOutbound: OutgoingStreamCountLimit,
   194  			StreamsInbound:  IncomingStreamCountLimit,
   195  		},
   196  	}
   197  
   198  	// Create our limits by using our cfg and replacing the default values with values from `scaledDefaultLimits`
   199  	limits := cfg.Build(rcmgr.InfiniteLimits)
   200  
   201  	// The resource manager expects a limiter, se we create one from our limits.
   202  	limiter := rcmgr.NewFixedLimiter(limits)
   203  
   204  	str, err := rcmgrObs.NewStatsTraceReporter()
   205  	if err != nil {
   206  		return nil, err
   207  	}
   208  
   209  	rm, err := rcmgr.NewResourceManager(limiter, rcmgr.WithTraceReporter(str))
   210  	if err != nil {
   211  		return nil, err
   212  	}
   213  
   214  	var natManager basichost.NATManager
   215  
   216  	opts := []libp2p.Option{
   217  		libp2p.ListenAddrStrings(listenAddrs...),
   218  		security,
   219  		// Use dedicated peerstore instead the global DefaultPeerstore
   220  		libp2p.Peerstore(libp2pPeerstore),
   221  		libp2p.UserAgent(userAgent()),
   222  		libp2p.ResourceManager(rm),
   223  	}
   224  
   225  	if o.NATAddr == "" {
   226  		opts = append(opts,
   227  			libp2p.NATManager(func(n network.Network) basichost.NATManager {
   228  				natManager = basichost.NewNATManager(n)
   229  				return natManager
   230  			}),
   231  		)
   232  	}
   233  
   234  	if o.PrivateKey != nil {
   235  		myKey, _, err := crypto.ECDSAKeyPairFromKey(o.PrivateKey)
   236  		if err != nil {
   237  			return nil, err
   238  		}
   239  		opts = append(opts,
   240  			libp2p.Identity(myKey),
   241  		)
   242  	}
   243  
   244  	transports := []libp2p.Option{
   245  		libp2p.Transport(tcp.NewTCPTransport, tcp.DisableReuseport()),
   246  	}
   247  
   248  	if o.EnableWS {
   249  		transports = append(transports, libp2p.Transport(ws.New))
   250  	}
   251  
   252  	opts = append(opts, transports...)
   253  
   254  	if o.hostFactory == nil {
   255  		// Use the default libp2p host creation
   256  		o.hostFactory = libp2p.New
   257  	}
   258  
   259  	h, err := o.hostFactory(opts...)
   260  	if err != nil {
   261  		return nil, err
   262  	}
   263  
   264  	// Support same non default security and transport options as
   265  	// original host.
   266  	dialer, err := o.hostFactory(append(transports, security)...)
   267  	if err != nil {
   268  		return nil, err
   269  	}
   270  
   271  	options := []autonat.Option{autonat.EnableService(dialer.Network())}
   272  
   273  	val, err := strconv.ParseBool(reachabilityOverridePublic)
   274  	if err != nil {
   275  		return nil, err
   276  	}
   277  	if val {
   278  		options = append(options, autonat.WithReachability(network.ReachabilityPublic))
   279  	}
   280  
   281  	// If you want to help other peers to figure out if they are behind
   282  	// NATs, you can launch the server-side of AutoNAT too (AutoRelay
   283  	// already runs the client)
   284  	var autoNAT autonat.AutoNAT
   285  	if autoNAT, err = autonat.New(h, options...); err != nil {
   286  		return nil, fmt.Errorf("autonat: %w", err)
   287  	}
   288  
   289  	if o.HeadersRWTimeout == 0 {
   290  		o.HeadersRWTimeout = defaultHeadersRWTimeout
   291  	}
   292  
   293  	var advertisableAddresser handshake.AdvertisableAddressResolver
   294  	var natAddrResolver *staticAddressResolver
   295  	if o.NATAddr == "" {
   296  		advertisableAddresser = &UpnpAddressResolver{
   297  			host: h,
   298  		}
   299  	} else {
   300  		natAddrResolver, err = newStaticAddressResolver(o.NATAddr, net.LookupIP)
   301  		if err != nil {
   302  			return nil, fmt.Errorf("static nat: %w", err)
   303  		}
   304  		advertisableAddresser = natAddrResolver
   305  	}
   306  
   307  	handshakeService, err := handshake.New(signer, advertisableAddresser, overlay, networkID, o.FullNode, o.Nonce, o.WelcomeMessage, o.ValidateOverlay, h.ID(), logger)
   308  	if err != nil {
   309  		return nil, fmt.Errorf("handshake service: %w", err)
   310  	}
   311  
   312  	// Create a new dialer for libp2p ping protocol. This ensures that the protocol
   313  	// uses a different set of keys to do ping. It prevents inconsistencies in peerstore as
   314  	// the addresses used are not dialable and hence should be cleaned up. We should create
   315  	// this host with the same transports and security options to be able to dial to other
   316  	// peers.
   317  	pingDialer, err := o.hostFactory(append(transports, security, libp2p.NoListenAddrs)...)
   318  	if err != nil {
   319  		return nil, err
   320  	}
   321  
   322  	peerRegistry := newPeerRegistry()
   323  	s := &Service{
   324  		ctx:               ctx,
   325  		host:              h,
   326  		natManager:        natManager,
   327  		natAddrResolver:   natAddrResolver,
   328  		autonatDialer:     dialer,
   329  		pingDialer:        pingDialer,
   330  		handshakeService:  handshakeService,
   331  		libp2pPeerstore:   libp2pPeerstore,
   332  		metrics:           newMetrics(),
   333  		networkID:         networkID,
   334  		peers:             peerRegistry,
   335  		addressbook:       ab,
   336  		blocklist:         blocklist.NewBlocklist(storer),
   337  		logger:            logger.WithName(loggerName).Register(),
   338  		tracer:            tracer,
   339  		connectionBreaker: breaker.NewBreaker(breaker.Options{}), // use default options
   340  		ready:             make(chan struct{}),
   341  		halt:              make(chan struct{}),
   342  		lightNodes:        lightNodes,
   343  		HeadersRWTimeout:  o.HeadersRWTimeout,
   344  		autoNAT:           autoNAT,
   345  	}
   346  
   347  	peerRegistry.setDisconnecter(s)
   348  
   349  	s.lightNodeLimit = defaultLightNodeLimit
   350  	if o.LightNodeLimit > 0 {
   351  		s.lightNodeLimit = o.LightNodeLimit
   352  	}
   353  
   354  	// Construct protocols.
   355  	id := protocol.ID(p2p.NewSwarmStreamName(handshake.ProtocolName, handshake.ProtocolVersion, handshake.StreamName))
   356  	matcher, err := s.protocolSemverMatcher(id)
   357  	if err != nil {
   358  		return nil, fmt.Errorf("protocol version match %s: %w", id, err)
   359  	}
   360  
   361  	s.host.SetStreamHandlerMatch(id, matcher, s.handleIncoming)
   362  
   363  	connMetricNotify := newConnMetricNotify(s.metrics)
   364  	h.Network().Notify(peerRegistry) // update peer registry on network events
   365  	h.Network().Notify(connMetricNotify)
   366  
   367  	return s, nil
   368  }
   369  
   370  func (s *Service) reachabilityWorker() error {
   371  	sub, err := s.host.EventBus().Subscribe([]interface{}{new(event.EvtLocalReachabilityChanged)})
   372  	if err != nil {
   373  		return fmt.Errorf("failed subscribing to reachability event %w", err)
   374  	}
   375  
   376  	go func() {
   377  		defer sub.Close()
   378  		for {
   379  			select {
   380  			case <-s.ctx.Done():
   381  				return
   382  			case e := <-sub.Out():
   383  				if r, ok := e.(event.EvtLocalReachabilityChanged); ok {
   384  					select {
   385  					case <-s.ready:
   386  					case <-s.halt:
   387  						return
   388  					}
   389  					s.logger.Debug("reachability changed", "new_reachability", r.Reachability.String())
   390  					s.notifier.UpdateReachability(p2p.ReachabilityStatus(r.Reachability))
   391  				}
   392  			}
   393  		}
   394  	}()
   395  	return nil
   396  }
   397  
   398  func (s *Service) handleIncoming(stream network.Stream) {
   399  	loggerV1 := s.logger.V(1).Register()
   400  
   401  	select {
   402  	case <-s.ready:
   403  	case <-s.halt:
   404  		_ = stream.Reset()
   405  		return
   406  	case <-s.ctx.Done():
   407  		_ = stream.Reset()
   408  		return
   409  	}
   410  
   411  	peerID := stream.Conn().RemotePeer()
   412  	handshakeStream := newStream(stream, s.metrics)
   413  	i, err := s.handshakeService.Handle(s.ctx, handshakeStream, stream.Conn().RemoteMultiaddr(), peerID)
   414  	if err != nil {
   415  		s.logger.Debug("stream handler: handshake: handle failed", "peer_id", peerID, "error", err)
   416  		s.logger.Error(nil, "stream handler: handshake: handle failed", "peer_id", peerID)
   417  		_ = handshakeStream.Reset()
   418  		_ = s.host.Network().ClosePeer(peerID)
   419  		return
   420  	}
   421  
   422  	overlay := i.BzzAddress.Overlay
   423  
   424  	blocked, err := s.blocklist.Exists(overlay)
   425  	if err != nil {
   426  		s.logger.Debug("stream handler: blocklisting: exists failed", "peer_address", overlay, "error", err)
   427  		s.logger.Error(nil, "stream handler: internal error while connecting with peer", "peer_address", overlay)
   428  		_ = handshakeStream.Reset()
   429  		_ = s.host.Network().ClosePeer(peerID)
   430  		return
   431  	}
   432  
   433  	if blocked {
   434  		s.logger.Error(nil, "stream handler: blocked connection from blocklisted peer", "peer_address", overlay)
   435  		_ = handshakeStream.Reset()
   436  		_ = s.host.Network().ClosePeer(peerID)
   437  		return
   438  	}
   439  
   440  	if exists := s.peers.addIfNotExists(stream.Conn(), overlay, i.FullNode); exists {
   441  		s.logger.Debug("stream handler: peer already exists", "peer_address", overlay)
   442  		if err = handshakeStream.FullClose(); err != nil {
   443  			s.logger.Debug("stream handler: could not close stream", "peer_address", overlay, "error", err)
   444  			s.logger.Error(nil, "stream handler: unable to handshake with peer", "peer_address", overlay)
   445  			_ = s.Disconnect(overlay, "unable to close handshake stream")
   446  		}
   447  		return
   448  	}
   449  
   450  	if err = handshakeStream.FullClose(); err != nil {
   451  		s.logger.Debug("stream handler: could not close stream", "peer_address", overlay, "error", err)
   452  		s.logger.Error(nil, "stream handler: unable to handshake with peer", "peer_address", overlay)
   453  		_ = s.Disconnect(overlay, "could not fully close stream on handshake")
   454  		return
   455  	}
   456  
   457  	if i.FullNode {
   458  		err = s.addressbook.Put(i.BzzAddress.Overlay, *i.BzzAddress)
   459  		if err != nil {
   460  			s.logger.Debug("stream handler: addressbook put error", "peer_id", peerID, "error", err)
   461  			s.logger.Error(nil, "stream handler: unable to persist peer", "peer_id", peerID)
   462  			_ = s.Disconnect(i.BzzAddress.Overlay, "unable to persist peer in addressbook")
   463  			return
   464  		}
   465  	}
   466  
   467  	peer := p2p.Peer{Address: overlay, FullNode: i.FullNode, EthereumAddress: i.BzzAddress.EthereumAddress}
   468  
   469  	s.protocolsmu.RLock()
   470  	for _, tn := range s.protocols {
   471  		if tn.ConnectIn != nil {
   472  			if err := tn.ConnectIn(s.ctx, peer); err != nil {
   473  				s.logger.Debug("stream handler: connectIn failed", "protocol", tn.Name, "version", tn.Version, "peer", overlay, "error", err)
   474  				_ = s.Disconnect(overlay, "failed to process inbound connection notifier")
   475  				s.protocolsmu.RUnlock()
   476  				return
   477  			}
   478  		}
   479  	}
   480  	s.protocolsmu.RUnlock()
   481  
   482  	if s.notifier != nil {
   483  		if !i.FullNode {
   484  			s.lightNodes.Connected(s.ctx, peer)
   485  			// light node announces explicitly
   486  			if err := s.notifier.Announce(s.ctx, peer.Address, i.FullNode); err != nil {
   487  				s.logger.Debug("stream handler: notifier.Announce failed", "peer", peer.Address, "error", err)
   488  			}
   489  
   490  			if s.lightNodes.Count() > s.lightNodeLimit {
   491  				// kick another node to fit this one in
   492  				p, err := s.lightNodes.RandomPeer(peer.Address)
   493  				if err != nil {
   494  					s.logger.Debug("stream handler: can't find a peer slot for light node", "error", err)
   495  					_ = s.Disconnect(peer.Address, "unable to find peer slot for light node")
   496  					return
   497  				} else {
   498  					loggerV1.Debug("stream handler: kicking away light node to make room for new node", "old_peer", p.String(), "new_peer", peer.Address)
   499  					s.metrics.KickedOutPeersCount.Inc()
   500  					_ = s.Disconnect(p, "kicking away light node to make room for peer")
   501  					return
   502  				}
   503  			}
   504  		} else {
   505  			if err := s.notifier.Connected(s.ctx, peer, false); err != nil {
   506  				s.logger.Debug("stream handler: notifier.Connected: peer disconnected", "peer", i.BzzAddress.Overlay, "error", err)
   507  				// note: this cannot be unit tested since the node
   508  				// waiting on handshakeStream.FullClose() on the other side
   509  				// might actually get a stream reset when we disconnect here
   510  				// resulting in a flaky response from the Connect method on
   511  				// the other side.
   512  				// that is why the Pick method has been added to the notifier
   513  				// interface, in addition to the possibility of deciding whether
   514  				// a peer connection is wanted prior to adding the peer to the
   515  				// peer registry and starting the protocols.
   516  				_ = s.Disconnect(overlay, "unable to signal connection notifier")
   517  				return
   518  			}
   519  			// when a full node connects, we gossip about it to the
   520  			// light nodes so that they can also have a chance at building
   521  			// a solid topology.
   522  			_ = s.lightNodes.EachPeer(func(addr swarm.Address, _ uint8) (bool, bool, error) {
   523  				go func(addressee, peer swarm.Address, fullnode bool) {
   524  					if err := s.notifier.AnnounceTo(s.ctx, addressee, peer, fullnode); err != nil {
   525  						s.logger.Debug("stream handler: notifier.AnnounceTo failed", "addressee", addressee, "peer", peer, "error", err)
   526  					}
   527  				}(addr, peer.Address, i.FullNode)
   528  				return false, false, nil
   529  			})
   530  		}
   531  	}
   532  
   533  	s.metrics.HandledStreamCount.Inc()
   534  	if !s.peers.Exists(overlay) {
   535  		s.logger.Warning("stream handler: inbound peer does not exist, disconnecting", "peer", overlay)
   536  		_ = s.Disconnect(overlay, "unknown inbound peer")
   537  		return
   538  	}
   539  
   540  	if s.reacher != nil {
   541  		s.reacher.Connected(overlay, i.BzzAddress.Underlay)
   542  	}
   543  
   544  	peerUserAgent := appendSpace(s.peerUserAgent(s.ctx, peerID))
   545  
   546  	loggerV1.Debug("stream handler: successfully connected to peer (inbound)", "addresses", i.BzzAddress.ShortString(), "light", i.LightString(), "user_agent", peerUserAgent)
   547  	s.logger.Debug("stream handler: successfully connected to peer (inbound)", "address", i.BzzAddress.Overlay, "light", i.LightString(), "user_agent", peerUserAgent)
   548  }
   549  
   550  func (s *Service) SetPickyNotifier(n p2p.PickyNotifier) {
   551  	s.handshakeService.SetPicker(n)
   552  	s.notifier = n
   553  	s.reacher = reacher.New(s, n, nil)
   554  }
   555  
   556  func (s *Service) AddProtocol(p p2p.ProtocolSpec) (err error) {
   557  	for _, ss := range p.StreamSpecs {
   558  		ss := ss
   559  		id := protocol.ID(p2p.NewSwarmStreamName(p.Name, p.Version, ss.Name))
   560  		matcher, err := s.protocolSemverMatcher(id)
   561  		if err != nil {
   562  			return fmt.Errorf("protocol version match %s: %w", id, err)
   563  		}
   564  
   565  		s.host.SetStreamHandlerMatch(id, matcher, func(streamlibp2p network.Stream) {
   566  			start := time.Now()
   567  			peerID := streamlibp2p.Conn().RemotePeer()
   568  			overlay, found := s.peers.overlay(peerID)
   569  			if !found {
   570  				_ = streamlibp2p.Reset()
   571  				s.logger.Debug("overlay address for peer not found", "peer_id", peerID)
   572  				return
   573  			}
   574  			full, found := s.peers.fullnode(peerID)
   575  			if !found {
   576  				_ = streamlibp2p.Reset()
   577  				s.logger.Debug("fullnode info for peer not found", "peer_id", peerID)
   578  				return
   579  			}
   580  
   581  			stream := newStream(streamlibp2p, s.metrics)
   582  
   583  			// exchange headers
   584  			ctx, cancel := context.WithTimeout(s.ctx, s.HeadersRWTimeout)
   585  			defer cancel()
   586  			if err := handleHeaders(ctx, ss.Headler, stream, overlay); err != nil {
   587  				s.logger.Debug("handle protocol: handle headers failed", "protocol", p.Name, "version", p.Version, "stream", ss.Name, "peer", overlay, "error", err)
   588  				_ = stream.Reset()
   589  				return
   590  			}
   591  			s.metrics.HeadersExchangeDuration.Observe(time.Since(start).Seconds())
   592  
   593  			ctx, cancel = context.WithCancel(s.ctx)
   594  
   595  			s.peers.addStream(peerID, streamlibp2p, cancel)
   596  			defer s.peers.removeStream(peerID, streamlibp2p)
   597  
   598  			// tracing: get span tracing context and add it to the context
   599  			// silently ignore if the peer is not providing tracing
   600  			ctx, err := s.tracer.WithContextFromHeaders(ctx, stream.Headers())
   601  			if err != nil && !errors.Is(err, tracing.ErrContextNotFound) {
   602  				s.logger.Debug("handle protocol: get tracing context failed", "protocol", p.Name, "version", p.Version, "stream", ss.Name, "peer", overlay, "error", err)
   603  				_ = stream.Reset()
   604  				return
   605  			}
   606  
   607  			logger := tracing.NewLoggerWithTraceID(ctx, s.logger)
   608  			loggerV1 := logger.V(1).Build()
   609  
   610  			s.metrics.HandledStreamCount.Inc()
   611  			if err := ss.Handler(ctx, p2p.Peer{Address: overlay, FullNode: full}, stream); err != nil {
   612  				var de *p2p.DisconnectError
   613  				if errors.As(err, &de) {
   614  					loggerV1.Debug("libp2p handler: disconnecting due to disconnect error", "protocol", p.Name, "address", overlay)
   615  					_ = stream.Reset()
   616  					_ = s.Disconnect(overlay, de.Error())
   617  				}
   618  
   619  				var bpe *p2p.BlockPeerError
   620  				if errors.As(err, &bpe) {
   621  					_ = stream.Reset()
   622  					if err := s.Blocklist(overlay, bpe.Duration(), bpe.Error()); err != nil {
   623  						logger.Debug("blocklist: could not blocklist peer", "peer_id", peerID, "error", err)
   624  						logger.Error(nil, "unable to blocklist peer", "peer_id", peerID)
   625  					}
   626  					loggerV1.Debug("handler: peer blocklisted", "protocol", p.Name, "peer_address", overlay)
   627  				}
   628  				// count unexpected requests
   629  				if errors.Is(err, p2p.ErrUnexpected) {
   630  					s.metrics.UnexpectedProtocolReqCount.Inc()
   631  				}
   632  				if errors.Is(err, network.ErrReset) {
   633  					s.metrics.StreamHandlerErrResetCount.Inc()
   634  				}
   635  				logger.Debug("handle protocol failed", "protocol", p.Name, "version", p.Version, "stream", ss.Name, "peer", overlay, "error", err)
   636  				return
   637  			}
   638  		})
   639  	}
   640  
   641  	s.protocolsmu.Lock()
   642  	s.protocols = append(s.protocols, p)
   643  	s.protocolsmu.Unlock()
   644  	return nil
   645  }
   646  
   647  func (s *Service) Addresses() (addresses []ma.Multiaddr, err error) {
   648  	for _, addr := range s.host.Addrs() {
   649  		a, err := buildUnderlayAddress(addr, s.host.ID())
   650  		if err != nil {
   651  			return nil, err
   652  		}
   653  
   654  		addresses = append(addresses, a)
   655  	}
   656  	if s.natAddrResolver != nil && len(addresses) > 0 {
   657  		a, err := s.natAddrResolver.Resolve(addresses[0])
   658  		if err != nil {
   659  			return nil, err
   660  		}
   661  		addresses = append(addresses, a)
   662  	}
   663  
   664  	return addresses, nil
   665  }
   666  
   667  func (s *Service) NATManager() basichost.NATManager {
   668  	return s.natManager
   669  }
   670  
   671  func (s *Service) Blocklist(overlay swarm.Address, duration time.Duration, reason string) error {
   672  	loggerV1 := s.logger.V(1).Register()
   673  
   674  	if s.NetworkStatus() != p2p.NetworkStatusAvailable {
   675  		return errors.New("cannot blocklist peer when network not available")
   676  	}
   677  
   678  	id, ok := s.peers.peerID(overlay)
   679  	if !ok {
   680  		return p2p.ErrPeerNotFound
   681  	}
   682  
   683  	full, _ := s.peers.fullnode(id)
   684  
   685  	loggerV1.Debug("libp2p blocklisting peer", "peer_address", overlay.String(), "duration", duration, "reason", reason)
   686  	if err := s.blocklist.Add(overlay, duration, reason, full); err != nil {
   687  		s.metrics.BlocklistedPeerErrCount.Inc()
   688  		_ = s.Disconnect(overlay, "failed blocklisting peer")
   689  		return fmt.Errorf("blocklist peer %s: %w", overlay, err)
   690  	}
   691  	s.metrics.BlocklistedPeerCount.Inc()
   692  
   693  	_ = s.Disconnect(overlay, reason)
   694  	return nil
   695  }
   696  
   697  func buildHostAddress(peerID libp2ppeer.ID) (ma.Multiaddr, error) {
   698  	return ma.NewMultiaddr(fmt.Sprintf("/p2p/%s", peerID.String()))
   699  }
   700  
   701  func buildUnderlayAddress(addr ma.Multiaddr, peerID libp2ppeer.ID) (ma.Multiaddr, error) {
   702  	// Build host multiaddress
   703  	hostAddr, err := buildHostAddress(peerID)
   704  	if err != nil {
   705  		return nil, err
   706  	}
   707  
   708  	return addr.Encapsulate(hostAddr), nil
   709  }
   710  
   711  func (s *Service) Connect(ctx context.Context, addr ma.Multiaddr) (address *bzz.Address, err error) {
   712  	loggerV1 := s.logger.V(1).Register()
   713  
   714  	defer func() {
   715  		err = s.determineCurrentNetworkStatus(err)
   716  	}()
   717  
   718  	// Extract the peer ID from the multiaddr.
   719  	info, err := libp2ppeer.AddrInfoFromP2pAddr(addr)
   720  	if err != nil {
   721  		return nil, fmt.Errorf("addr from p2p: %w", err)
   722  	}
   723  
   724  	hostAddr, err := buildHostAddress(info.ID)
   725  	if err != nil {
   726  		return nil, fmt.Errorf("build host address: %w", err)
   727  	}
   728  
   729  	remoteAddr := addr.Decapsulate(hostAddr)
   730  
   731  	if overlay, found := s.peers.isConnected(info.ID, remoteAddr); found {
   732  		address = &bzz.Address{
   733  			Overlay:  overlay,
   734  			Underlay: addr,
   735  		}
   736  		return address, p2p.ErrAlreadyConnected
   737  	}
   738  
   739  	if err := s.connectionBreaker.Execute(func() error { return s.host.Connect(ctx, *info) }); err != nil {
   740  		if errors.Is(err, breaker.ErrClosed) {
   741  			s.metrics.ConnectBreakerCount.Inc()
   742  			return nil, p2p.NewConnectionBackoffError(err, s.connectionBreaker.ClosedUntil())
   743  		}
   744  		return nil, err
   745  	}
   746  
   747  	stream, err := s.newStreamForPeerID(ctx, info.ID, handshake.ProtocolName, handshake.ProtocolVersion, handshake.StreamName)
   748  	if err != nil {
   749  		_ = s.host.Network().ClosePeer(info.ID)
   750  		return nil, fmt.Errorf("connect new stream: %w", err)
   751  	}
   752  
   753  	handshakeStream := newStream(stream, s.metrics)
   754  	i, err := s.handshakeService.Handshake(ctx, handshakeStream, stream.Conn().RemoteMultiaddr(), stream.Conn().RemotePeer())
   755  	if err != nil {
   756  		_ = handshakeStream.Reset()
   757  		_ = s.host.Network().ClosePeer(info.ID)
   758  		return nil, fmt.Errorf("handshake: %w", err)
   759  	}
   760  
   761  	if !i.FullNode {
   762  		_ = handshakeStream.Reset()
   763  		_ = s.host.Network().ClosePeer(info.ID)
   764  		return nil, p2p.ErrDialLightNode
   765  	}
   766  
   767  	overlay := i.BzzAddress.Overlay
   768  
   769  	blocked, err := s.blocklist.Exists(overlay)
   770  	if err != nil {
   771  		s.logger.Debug("blocklisting: exists failed", "peer_id", info.ID, "error", err)
   772  		s.logger.Error(nil, "internal error while connecting with peer", "peer_id", info.ID)
   773  		_ = handshakeStream.Reset()
   774  		_ = s.host.Network().ClosePeer(info.ID)
   775  		return nil, err
   776  	}
   777  
   778  	if blocked {
   779  		s.logger.Error(nil, "blocked connection to blocklisted peer", "peer_id", info.ID)
   780  		_ = handshakeStream.Reset()
   781  		_ = s.host.Network().ClosePeer(info.ID)
   782  		return nil, p2p.ErrPeerBlocklisted
   783  	}
   784  
   785  	if exists := s.peers.addIfNotExists(stream.Conn(), overlay, i.FullNode); exists {
   786  		if err := handshakeStream.FullClose(); err != nil {
   787  			_ = s.Disconnect(overlay, "failed closing handshake stream after connect")
   788  			return nil, fmt.Errorf("peer exists, full close: %w", err)
   789  		}
   790  
   791  		return i.BzzAddress, nil
   792  	}
   793  
   794  	if err := handshakeStream.FullClose(); err != nil {
   795  		_ = s.Disconnect(overlay, "could not fully close handshake stream after connect")
   796  		return nil, fmt.Errorf("connect full close %w", err)
   797  	}
   798  
   799  	if i.FullNode {
   800  		err = s.addressbook.Put(overlay, *i.BzzAddress)
   801  		if err != nil {
   802  			_ = s.Disconnect(overlay, "failed storing peer in addressbook")
   803  			return nil, fmt.Errorf("storing bzz address: %w", err)
   804  		}
   805  	}
   806  
   807  	s.protocolsmu.RLock()
   808  	for _, tn := range s.protocols {
   809  		if tn.ConnectOut != nil {
   810  			if err := tn.ConnectOut(ctx, p2p.Peer{Address: overlay, FullNode: i.FullNode, EthereumAddress: i.BzzAddress.EthereumAddress}); err != nil {
   811  				s.logger.Debug("connectOut: failed to connect", "protocol", tn.Name, "version", tn.Version, "peer", overlay, "error", err)
   812  				_ = s.Disconnect(overlay, "failed to process outbound connection notifier")
   813  				s.protocolsmu.RUnlock()
   814  				return nil, fmt.Errorf("connectOut: protocol: %s, version:%s: %w", tn.Name, tn.Version, err)
   815  			}
   816  		}
   817  	}
   818  	s.protocolsmu.RUnlock()
   819  
   820  	if !s.peers.Exists(overlay) {
   821  		_ = s.Disconnect(overlay, "outbound peer does not exist")
   822  		return nil, fmt.Errorf("libp2p connect: peer %s does not exist %w", overlay, p2p.ErrPeerNotFound)
   823  	}
   824  
   825  	s.metrics.CreatedConnectionCount.Inc()
   826  
   827  	if s.reacher != nil {
   828  		s.reacher.Connected(overlay, i.BzzAddress.Underlay)
   829  	}
   830  
   831  	peerUserAgent := appendSpace(s.peerUserAgent(ctx, info.ID))
   832  
   833  	loggerV1.Debug("successfully connected to peer (outbound)", "addresses", i.BzzAddress.ShortString(), "light", i.LightString(), "user_agent", peerUserAgent)
   834  	s.logger.Debug("successfully connected to peer (outbound)", "address", i.BzzAddress.Overlay, "light", i.LightString(), "user_agent", peerUserAgent)
   835  	return i.BzzAddress, nil
   836  }
   837  
   838  func (s *Service) Disconnect(overlay swarm.Address, reason string) (err error) {
   839  	s.metrics.DisconnectCount.Inc()
   840  
   841  	s.logger.Debug("libp2p disconnect: disconnecting peer", "peer_address", overlay, "reason", reason)
   842  
   843  	// found is checked at the bottom of the function
   844  	found, full, peerID := s.peers.remove(overlay)
   845  
   846  	_ = s.host.Network().ClosePeer(peerID)
   847  
   848  	peer := p2p.Peer{Address: overlay, FullNode: full}
   849  
   850  	s.protocolsmu.RLock()
   851  	for _, tn := range s.protocols {
   852  		if tn.DisconnectOut != nil {
   853  			if err := tn.DisconnectOut(peer); err != nil {
   854  				s.logger.Debug("disconnectOut failed", "protocol", tn.Name, "version", tn.Version, "peer", overlay, "error", err)
   855  			}
   856  		}
   857  	}
   858  	s.protocolsmu.RUnlock()
   859  
   860  	if s.notifier != nil {
   861  		s.notifier.Disconnected(peer)
   862  	}
   863  	if s.lightNodes != nil {
   864  		s.lightNodes.Disconnected(peer)
   865  	}
   866  	if s.reacher != nil {
   867  		s.reacher.Disconnected(overlay)
   868  	}
   869  
   870  	if !found {
   871  		s.logger.Debug("libp2p disconnect: peer not found", "peer_address", overlay)
   872  		return p2p.ErrPeerNotFound
   873  	}
   874  
   875  	return nil
   876  }
   877  
   878  // disconnected is a registered peer registry event
   879  func (s *Service) disconnected(address swarm.Address) {
   880  	peer := p2p.Peer{Address: address}
   881  	peerID, found := s.peers.peerID(address)
   882  	if found {
   883  		// peerID might not always be found on shutdown
   884  		full, found := s.peers.fullnode(peerID)
   885  		if found {
   886  			peer.FullNode = full
   887  		}
   888  	}
   889  	s.protocolsmu.RLock()
   890  	for _, tn := range s.protocols {
   891  		if tn.DisconnectIn != nil {
   892  			if err := tn.DisconnectIn(peer); err != nil {
   893  				s.logger.Debug("disconnectIn failed", tn.Name, "version", tn.Version, "peer", address, "error", err)
   894  			}
   895  		}
   896  	}
   897  
   898  	s.protocolsmu.RUnlock()
   899  
   900  	if s.notifier != nil {
   901  		s.notifier.Disconnected(peer)
   902  	}
   903  	if s.lightNodes != nil {
   904  		s.lightNodes.Disconnected(peer)
   905  	}
   906  	if s.reacher != nil {
   907  		s.reacher.Disconnected(address)
   908  	}
   909  }
   910  
   911  func (s *Service) Peers() []p2p.Peer {
   912  	return s.peers.peers()
   913  }
   914  
   915  func (s *Service) Blocklisted(overlay swarm.Address) (bool, error) {
   916  	return s.blocklist.Exists(overlay)
   917  }
   918  
   919  func (s *Service) BlocklistedPeers() ([]p2p.BlockListedPeer, error) {
   920  	return s.blocklist.Peers()
   921  }
   922  
   923  func (s *Service) NewStream(ctx context.Context, overlay swarm.Address, headers p2p.Headers, protocolName, protocolVersion, streamName string) (p2p.Stream, error) {
   924  	select {
   925  	case <-ctx.Done():
   926  		return nil, ctx.Err()
   927  	default:
   928  	}
   929  
   930  	peerID, found := s.peers.peerID(overlay)
   931  	if !found {
   932  		return nil, p2p.ErrPeerNotFound
   933  	}
   934  
   935  	streamlibp2p, err := s.newStreamForPeerID(ctx, peerID, protocolName, protocolVersion, streamName)
   936  	if err != nil {
   937  		return nil, fmt.Errorf("new stream for peerid: %w", err)
   938  	}
   939  
   940  	stream := newStream(streamlibp2p, s.metrics)
   941  
   942  	// tracing: add span context header
   943  	if headers == nil {
   944  		headers = make(p2p.Headers)
   945  	}
   946  	if err := s.tracer.AddContextHeader(ctx, headers); err != nil && !errors.Is(err, tracing.ErrContextNotFound) {
   947  		_ = stream.Reset()
   948  		return nil, fmt.Errorf("new stream add context header fail: %w", err)
   949  	}
   950  
   951  	// exchange headers
   952  	ctx, cancel := context.WithTimeout(ctx, s.HeadersRWTimeout)
   953  	defer cancel()
   954  	if err := sendHeaders(ctx, headers, stream); err != nil {
   955  		_ = stream.Reset()
   956  		return nil, fmt.Errorf("send headers: %w", err)
   957  	}
   958  
   959  	return stream, nil
   960  }
   961  
   962  func (s *Service) newStreamForPeerID(ctx context.Context, peerID libp2ppeer.ID, protocolName, protocolVersion, streamName string) (network.Stream, error) {
   963  	swarmStreamName := p2p.NewSwarmStreamName(protocolName, protocolVersion, streamName)
   964  	st, err := s.host.NewStream(ctx, peerID, protocol.ID(swarmStreamName))
   965  	if err != nil {
   966  		if st != nil {
   967  			s.logger.Debug("stream experienced unexpected early close")
   968  			_ = st.Close()
   969  		}
   970  		var errNotSupported multistream.ErrNotSupported[protocol.ID]
   971  		if errors.As(err, &errNotSupported) {
   972  			return nil, p2p.NewIncompatibleStreamError(err)
   973  		}
   974  		if errors.Is(err, multistream.ErrIncorrectVersion) {
   975  			return nil, p2p.NewIncompatibleStreamError(err)
   976  		}
   977  		return nil, fmt.Errorf("create stream %s to %s: %w", swarmStreamName, peerID, err)
   978  	}
   979  	s.metrics.CreatedStreamCount.Inc()
   980  	return st, nil
   981  }
   982  
   983  func (s *Service) Close() error {
   984  	if err := s.libp2pPeerstore.Close(); err != nil {
   985  		return err
   986  	}
   987  	if s.natManager != nil {
   988  		if err := s.natManager.Close(); err != nil {
   989  			return err
   990  		}
   991  	}
   992  	if err := s.autonatDialer.Close(); err != nil {
   993  		return err
   994  	}
   995  	if err := s.pingDialer.Close(); err != nil {
   996  		return err
   997  	}
   998  	if s.reacher != nil {
   999  		if err := s.reacher.Close(); err != nil {
  1000  			return err
  1001  		}
  1002  	}
  1003  	if s.autoNAT != nil {
  1004  		if err := s.autoNAT.Close(); err != nil {
  1005  			return err
  1006  		}
  1007  	}
  1008  
  1009  	return s.host.Close()
  1010  }
  1011  
  1012  // SetWelcomeMessage sets the welcome message for the handshake protocol.
  1013  func (s *Service) SetWelcomeMessage(val string) error {
  1014  	return s.handshakeService.SetWelcomeMessage(val)
  1015  }
  1016  
  1017  // GetWelcomeMessage returns the value of the welcome message.
  1018  func (s *Service) GetWelcomeMessage() string {
  1019  	return s.handshakeService.GetWelcomeMessage()
  1020  }
  1021  
  1022  func (s *Service) Ready() error {
  1023  	if err := s.reachabilityWorker(); err != nil {
  1024  		return fmt.Errorf("reachability worker: %w", err)
  1025  	}
  1026  
  1027  	close(s.ready)
  1028  	return nil
  1029  }
  1030  
  1031  func (s *Service) Halt() {
  1032  	close(s.halt)
  1033  }
  1034  
  1035  func (s *Service) Ping(ctx context.Context, addr ma.Multiaddr) (rtt time.Duration, err error) {
  1036  	info, err := libp2ppeer.AddrInfoFromP2pAddr(addr)
  1037  	if err != nil {
  1038  		return rtt, fmt.Errorf("unable to parse underlay address: %w", err)
  1039  	}
  1040  
  1041  	// Add the address to libp2p peerstore for it to be dialable
  1042  	s.pingDialer.Peerstore().AddAddrs(info.ID, info.Addrs, peerstore.TempAddrTTL)
  1043  
  1044  	// Cleanup connection after ping is done
  1045  	defer func() {
  1046  		_ = s.pingDialer.Network().ClosePeer(info.ID)
  1047  	}()
  1048  
  1049  	select {
  1050  	case <-ctx.Done():
  1051  		return rtt, ctx.Err()
  1052  	case res := <-libp2pping.Ping(ctx, s.pingDialer, info.ID):
  1053  		return res.RTT, res.Error
  1054  	}
  1055  }
  1056  
  1057  // peerUserAgent returns User Agent string of the connected peer if the peer
  1058  // provides it. It ignores the default libp2p user agent string
  1059  // "github.com/libp2p/go-libp2p" and returns empty string in that case.
  1060  func (s *Service) peerUserAgent(ctx context.Context, peerID libp2ppeer.ID) string {
  1061  	ctx, cancel := context.WithTimeout(ctx, peerUserAgentTimeout)
  1062  	defer cancel()
  1063  	var (
  1064  		v   interface{}
  1065  		err error
  1066  	)
  1067  	// Peerstore may not contain all keys and values right after the connections is created.
  1068  	// This retry mechanism ensures more reliable user agent propagation.
  1069  	for iterate := true; iterate; {
  1070  		v, err = s.host.Peerstore().Get(peerID, "AgentVersion")
  1071  		if err == nil {
  1072  			break
  1073  		}
  1074  		select {
  1075  		case <-ctx.Done():
  1076  			iterate = false
  1077  		case <-time.After(50 * time.Millisecond):
  1078  		}
  1079  	}
  1080  	if err != nil {
  1081  		// error is ignored as user agent is informative only
  1082  		return ""
  1083  	}
  1084  	ua, ok := v.(string)
  1085  	if !ok {
  1086  		return ""
  1087  	}
  1088  	// Ignore the default user agent.
  1089  	if ua == "github.com/libp2p/go-libp2p" {
  1090  		return ""
  1091  	}
  1092  	return ua
  1093  }
  1094  
  1095  // NetworkStatus implements the p2p.NetworkStatuser interface.
  1096  func (s *Service) NetworkStatus() p2p.NetworkStatus {
  1097  	return p2p.NetworkStatus(s.networkStatus.Load())
  1098  }
  1099  
  1100  // determineCurrentNetworkStatus determines if the network
  1101  // is available/unavailable based on the given error, and
  1102  // returns ErrNetworkUnavailable if unavailable.
  1103  // The result of this operation is stored and can be reflected
  1104  // in the results of future NetworkStatus method calls.
  1105  func (s *Service) determineCurrentNetworkStatus(err error) error {
  1106  	switch {
  1107  	case err == nil:
  1108  		s.networkStatus.Store(int32(p2p.NetworkStatusAvailable))
  1109  	case errors.Is(err, lp2pswarm.ErrDialBackoff):
  1110  		if s.NetworkStatus() == p2p.NetworkStatusUnavailable {
  1111  			err = errors.Join(err, p2p.ErrNetworkUnavailable)
  1112  		}
  1113  	case isNetworkOrHostUnreachableError(err):
  1114  		s.networkStatus.Store(int32(p2p.NetworkStatusUnavailable))
  1115  		err = errors.Join(err, p2p.ErrNetworkUnavailable)
  1116  	default:
  1117  		err = fmt.Errorf("network status unknown: %w", err)
  1118  	}
  1119  	return err
  1120  }
  1121  
  1122  // appendSpace adds a leading space character if the string is not empty.
  1123  // It is useful for constructing log messages with conditional substrings.
  1124  func appendSpace(s string) string {
  1125  	if s == "" {
  1126  		return ""
  1127  	}
  1128  	return " " + s
  1129  }
  1130  
  1131  // userAgent returns a User Agent string passed to the libp2p host to identify peer node.
  1132  func userAgent() string {
  1133  	return fmt.Sprintf("bee/%s %s %s/%s", bee.Version, runtime.Version(), runtime.GOOS, runtime.GOARCH)
  1134  }
  1135  
  1136  func newConnMetricNotify(m metrics) *connectionNotifier {
  1137  	return &connectionNotifier{
  1138  		metrics:  m,
  1139  		Notifiee: new(network.NoopNotifiee),
  1140  	}
  1141  }
  1142  
  1143  type connectionNotifier struct {
  1144  	metrics metrics
  1145  	network.Notifiee
  1146  }
  1147  
  1148  func (c *connectionNotifier) Connected(_ network.Network, _ network.Conn) {
  1149  	c.metrics.HandledConnectionCount.Inc()
  1150  }
  1151  
  1152  // isNetworkOrHostUnreachableError determines based on the
  1153  // given error whether the host or network is reachable.
  1154  func isNetworkOrHostUnreachableError(err error) bool {
  1155  	var de *lp2pswarm.DialError
  1156  	if !errors.As(err, &de) {
  1157  		return false
  1158  	}
  1159  
  1160  	// Since TransportError doesn't implement the Unwrap
  1161  	// method we need to inspect the errors manually.
  1162  	for i := range de.DialErrors {
  1163  		var te *lp2pswarm.TransportError
  1164  		if !errors.As(&de.DialErrors[i], &te) {
  1165  			continue
  1166  		}
  1167  
  1168  		var ne *net.OpError
  1169  		if !errors.As(te.Cause, &ne) || ne.Op != "dial" {
  1170  			continue
  1171  		}
  1172  
  1173  		var se *os.SyscallError
  1174  		if errors.As(ne, &se) && strings.HasPrefix(se.Syscall, "connect") &&
  1175  			(errors.Is(se.Err, errHostUnreachable) || errors.Is(se.Err, errNetworkUnreachable)) {
  1176  			return true
  1177  		}
  1178  	}
  1179  	return false
  1180  }