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 }