diff --git a/.gitignore b/.gitignore index 64c6d853dd..591cbdecd7 100644 --- a/.gitignore +++ b/.gitignore @@ -4,3 +4,5 @@ *.sqlog *.qlog.zst *.sqlog.zst + +examples/webtransport-wasm/*.wasm \ No newline at end of file diff --git a/config/config.go b/config/config.go index 607dceba49..ea638145a2 100644 --- a/config/config.go +++ b/config/config.go @@ -1,12 +1,13 @@ +//go:build !js +// +build !js + package config import ( "context" - "crypto/rand" "errors" "fmt" "net" - "slices" "time" "github.com/libp2p/go-libp2p/core/connmgr" @@ -18,17 +19,13 @@ import ( "github.com/libp2p/go-libp2p/core/peer" "github.com/libp2p/go-libp2p/core/peerstore" "github.com/libp2p/go-libp2p/core/pnet" - "github.com/libp2p/go-libp2p/core/protocol" "github.com/libp2p/go-libp2p/core/routing" "github.com/libp2p/go-libp2p/core/sec" "github.com/libp2p/go-libp2p/core/sec/insecure" "github.com/libp2p/go-libp2p/core/transport" - "github.com/libp2p/go-libp2p/p2p/host/autonat" "github.com/libp2p/go-libp2p/p2p/host/autorelay" bhost "github.com/libp2p/go-libp2p/p2p/host/basic" - blankhost "github.com/libp2p/go-libp2p/p2p/host/blank" "github.com/libp2p/go-libp2p/p2p/host/eventbus" - "github.com/libp2p/go-libp2p/p2p/host/peerstore/pstoremem" rcmgr "github.com/libp2p/go-libp2p/p2p/host/resource-manager" routed "github.com/libp2p/go-libp2p/p2p/host/routed" "github.com/libp2p/go-libp2p/p2p/net/swarm" @@ -40,38 +37,14 @@ import ( "github.com/libp2p/go-libp2p/p2p/transport/quicreuse" "github.com/libp2p/go-libp2p/p2p/transport/tcpreuse" libp2pwebrtc "github.com/libp2p/go-libp2p/p2p/transport/webrtc" - "github.com/prometheus/client_golang/prometheus" - ma "github.com/multiformats/go-multiaddr" manet "github.com/multiformats/go-multiaddr/net" + "github.com/prometheus/client_golang/prometheus" "github.com/quic-go/quic-go" "go.uber.org/fx" "go.uber.org/fx/fxevent" ) -// AddrsFactory is a function that takes a set of multiaddrs we're listening on and -// returns the set of multiaddrs we should advertise to the network. -type AddrsFactory = bhost.AddrsFactory - -// NATManagerC is a NATManager constructor. -type NATManagerC func(network.Network) bhost.NATManager - -type RoutingC func(host.Host) (routing.PeerRouting, error) - -// AutoNATConfig defines the AutoNAT behavior for the libp2p host. -type AutoNATConfig struct { - ForceReachability *network.Reachability - EnableService bool - ThrottleGlobalLimit int - ThrottlePeerLimit int - ThrottleInterval time.Duration -} - -type Security struct { - ID protocol.ID - Constructor interface{} -} - // Config describes a set of settings for a libp2p node // // This is *not* a stable interface. Use the options defined in the root @@ -150,140 +123,6 @@ type Config struct { ShareTCPListener bool } -func (cfg *Config) makeSwarm(eventBus event.Bus, enableMetrics bool) (*swarm.Swarm, error) { - if cfg.Peerstore == nil { - return nil, fmt.Errorf("no peerstore specified") - } - - // Check this early. Prevents us from even *starting* without verifying this. - if pnet.ForcePrivateNetwork && len(cfg.PSK) == 0 { - log.Error("tried to create a libp2p node with no Private" + - " Network Protector but usage of Private Networks" + - " is forced by the environment") - // Note: This is *also* checked the upgrader itself, so it'll be - // enforced even *if* you don't use the libp2p constructor. - return nil, pnet.ErrNotInPrivateNetwork - } - - if cfg.PeerKey == nil { - return nil, fmt.Errorf("no peer key specified") - } - - // Obtain Peer ID from public key - pid, err := peer.IDFromPublicKey(cfg.PeerKey.GetPublic()) - if err != nil { - return nil, err - } - - if err := cfg.Peerstore.AddPrivKey(pid, cfg.PeerKey); err != nil { - return nil, err - } - if err := cfg.Peerstore.AddPubKey(pid, cfg.PeerKey.GetPublic()); err != nil { - return nil, err - } - - opts := append(cfg.SwarmOpts, - swarm.WithUDPBlackHoleSuccessCounter(cfg.UDPBlackHoleSuccessCounter), - swarm.WithIPv6BlackHoleSuccessCounter(cfg.IPv6BlackHoleSuccessCounter), - ) - if cfg.Reporter != nil { - opts = append(opts, swarm.WithMetrics(cfg.Reporter)) - } - if cfg.ConnectionGater != nil { - opts = append(opts, swarm.WithConnectionGater(cfg.ConnectionGater)) - } - if cfg.DialTimeout != 0 { - opts = append(opts, swarm.WithDialTimeout(cfg.DialTimeout)) - } - if cfg.ResourceManager != nil { - opts = append(opts, swarm.WithResourceManager(cfg.ResourceManager)) - } - if cfg.MultiaddrResolver != nil { - opts = append(opts, swarm.WithMultiaddrResolver(cfg.MultiaddrResolver)) - } - if cfg.DialRanker != nil { - opts = append(opts, swarm.WithDialRanker(cfg.DialRanker)) - } - - if enableMetrics { - opts = append(opts, - swarm.WithMetricsTracer(swarm.NewMetricsTracer(swarm.WithRegisterer(cfg.PrometheusRegisterer)))) - } - // TODO: Make the swarm implementation configurable. - return swarm.NewSwarm(pid, cfg.Peerstore, eventBus, opts...) -} - -func (cfg *Config) makeAutoNATV2Host() (host.Host, error) { - autonatPrivKey, _, err := crypto.GenerateEd25519Key(rand.Reader) - if err != nil { - return nil, err - } - ps, err := pstoremem.NewPeerstore() - if err != nil { - return nil, err - } - - autoNatCfg := Config{ - Transports: cfg.Transports, - Muxers: cfg.Muxers, - SecurityTransports: cfg.SecurityTransports, - Insecure: cfg.Insecure, - PSK: cfg.PSK, - ConnectionGater: cfg.ConnectionGater, - Reporter: cfg.Reporter, - PeerKey: autonatPrivKey, - Peerstore: ps, - DialRanker: swarm.NoDelayDialRanker, - UDPBlackHoleSuccessCounter: cfg.UDPBlackHoleSuccessCounter, - IPv6BlackHoleSuccessCounter: cfg.IPv6BlackHoleSuccessCounter, - ResourceManager: cfg.ResourceManager, - SwarmOpts: []swarm.Option{ - // Don't update black hole state for failed autonat dials - swarm.WithReadOnlyBlackHoleDetector(), - }, - } - fxopts, err := autoNatCfg.addTransports() - if err != nil { - return nil, err - } - var dialerHost host.Host - fxopts = append(fxopts, - fx.Provide(eventbus.NewBus), - fx.Provide(func(lifecycle fx.Lifecycle, b event.Bus) (*swarm.Swarm, error) { - lifecycle.Append(fx.Hook{ - OnStop: func(context.Context) error { - return ps.Close() - }}) - sw, err := autoNatCfg.makeSwarm(b, false) - return sw, err - }), - fx.Provide(func(sw *swarm.Swarm) *blankhost.BlankHost { - return blankhost.NewBlankHost(sw) - }), - fx.Provide(func(bh *blankhost.BlankHost) host.Host { - return bh - }), - fx.Provide(func() crypto.PrivKey { return autonatPrivKey }), - fx.Provide(func(bh host.Host) peer.ID { return bh.ID() }), - fx.Invoke(func(bh *blankhost.BlankHost) { - dialerHost = bh - }), - ) - app := fx.New(fxopts...) - if err := app.Err(); err != nil { - return nil, err - } - err = app.Start(context.Background()) - if err != nil { - return nil, err - } - go func() { - <-dialerHost.Network().(*swarm.Swarm).Done() - app.Stop(context.Background()) - }() - return dialerHost, nil -} - func (cfg *Config) addTransports() ([]fx.Option, error) { fxopts := []fx.Option{ fx.WithLogger(func() fxevent.Logger { return getFXLogger() }), @@ -413,59 +252,6 @@ func (cfg *Config) addTransports() ([]fx.Option, error) { return fxopts, nil } -func (cfg *Config) newBasicHost(swrm *swarm.Swarm, eventBus event.Bus) (*bhost.BasicHost, error) { - var autonatv2Dialer host.Host - if cfg.EnableAutoNATv2 { - ah, err := cfg.makeAutoNATV2Host() - if err != nil { - return nil, err - } - autonatv2Dialer = ah - } - h, err := bhost.NewHost(swrm, &bhost.HostOpts{ - EventBus: eventBus, - ConnManager: cfg.ConnManager, - AddrsFactory: cfg.AddrsFactory, - NATManager: cfg.NATManager, - EnablePing: !cfg.DisablePing, - UserAgent: cfg.UserAgent, - ProtocolVersion: cfg.ProtocolVersion, - EnableHolePunching: cfg.EnableHolePunching, - HolePunchingOptions: cfg.HolePunchingOptions, - EnableRelayService: cfg.EnableRelayService, - RelayServiceOpts: cfg.RelayServiceOpts, - EnableMetrics: !cfg.DisableMetrics, - PrometheusRegisterer: cfg.PrometheusRegisterer, - DisableIdentifyAddressDiscovery: cfg.DisableIdentifyAddressDiscovery, - EnableAutoNATv2: cfg.EnableAutoNATv2, - AutoNATv2Dialer: autonatv2Dialer, - }) - if err != nil { - return nil, err - } - return h, nil -} - -func (cfg *Config) validate() error { - if cfg.EnableAutoRelay && !cfg.Relay { - return fmt.Errorf("cannot enable autorelay; relay is not enabled") - } - // If possible check that the resource manager conn limit is higher than the - // limit set in the conn manager. - if l, ok := cfg.ResourceManager.(connmgr.GetConnLimiter); ok { - err := cfg.ConnManager.CheckLimit(l) - if err != nil { - log.Warn(fmt.Sprintf("rcmgr limit conflicts with connmgr limit: %v", err)) - } - } - - if len(cfg.PSK) > 0 && cfg.ShareTCPListener { - return errors.New("cannot use shared TCP listener with PSK") - } - - return nil -} - // NewNode constructs a new libp2p Host from the Config. // // This function consumes the config. Do not reuse it (really!). @@ -597,123 +383,3 @@ func (cfg *Config) NewNode() (host.Host, error) { } return &closableBasicHost{App: app, BasicHost: bh}, nil } - -func (cfg *Config) addAutoNAT(h *bhost.BasicHost) error { - // Only use public addresses for autonat - addrFunc := func() []ma.Multiaddr { - return slices.DeleteFunc(h.AllAddrs(), func(a ma.Multiaddr) bool { return !manet.IsPublicAddr(a) }) - } - if cfg.AddrsFactory != nil { - addrFunc = func() []ma.Multiaddr { - return slices.DeleteFunc( - slices.Clone(cfg.AddrsFactory(h.AllAddrs())), - func(a ma.Multiaddr) bool { return !manet.IsPublicAddr(a) }) - } - } - autonatOpts := []autonat.Option{ - autonat.UsingAddresses(addrFunc), - } - if !cfg.DisableMetrics { - autonatOpts = append(autonatOpts, autonat.WithMetricsTracer( - autonat.NewMetricsTracer(autonat.WithRegisterer(cfg.PrometheusRegisterer)), - )) - } - if cfg.AutoNATConfig.ThrottleInterval != 0 { - autonatOpts = append(autonatOpts, - autonat.WithThrottling(cfg.AutoNATConfig.ThrottleGlobalLimit, cfg.AutoNATConfig.ThrottleInterval), - autonat.WithPeerThrottling(cfg.AutoNATConfig.ThrottlePeerLimit)) - } - if cfg.AutoNATConfig.EnableService { - autonatPrivKey, _, err := crypto.GenerateEd25519Key(rand.Reader) - if err != nil { - return err - } - ps, err := pstoremem.NewPeerstore() - if err != nil { - return err - } - - // Pull out the pieces of the config that we _actually_ care about. - // Specifically, don't set up things like listeners, identify, etc. - autoNatCfg := Config{ - Transports: cfg.Transports, - Muxers: cfg.Muxers, - SecurityTransports: cfg.SecurityTransports, - Insecure: cfg.Insecure, - PSK: cfg.PSK, - ConnectionGater: cfg.ConnectionGater, - Reporter: cfg.Reporter, - PeerKey: autonatPrivKey, - Peerstore: ps, - DialRanker: swarm.NoDelayDialRanker, - ResourceManager: cfg.ResourceManager, - SwarmOpts: []swarm.Option{ - swarm.WithUDPBlackHoleSuccessCounter(nil), - swarm.WithIPv6BlackHoleSuccessCounter(nil), - }, - } - - fxopts, err := autoNatCfg.addTransports() - if err != nil { - return err - } - var dialer *swarm.Swarm - - fxopts = append(fxopts, - fx.Provide(eventbus.NewBus), - fx.Provide(func(lifecycle fx.Lifecycle, b event.Bus) (*swarm.Swarm, error) { - lifecycle.Append(fx.Hook{ - OnStop: func(context.Context) error { - return ps.Close() - }}) - var err error - dialer, err = autoNatCfg.makeSwarm(b, false) - return dialer, err - - }), - fx.Provide(func(s *swarm.Swarm) peer.ID { return s.LocalPeer() }), - fx.Provide(func() crypto.PrivKey { return autonatPrivKey }), - ) - app := fx.New(fxopts...) - if err := app.Err(); err != nil { - return err - } - err = app.Start(context.Background()) - if err != nil { - return err - } - go func() { - <-dialer.Done() // The swarm used for autonat has closed, we can cleanup now - app.Stop(context.Background()) - }() - autonatOpts = append(autonatOpts, autonat.EnableService(dialer)) - } - if cfg.AutoNATConfig.ForceReachability != nil { - autonatOpts = append(autonatOpts, autonat.WithReachability(*cfg.AutoNATConfig.ForceReachability)) - } - - autonat, err := autonat.New(h, autonatOpts...) - if err != nil { - return fmt.Errorf("autonat init failed: %w", err) - } - h.SetAutoNat(autonat) - return nil -} - -// Option is a libp2p config option that can be given to the libp2p constructor -// (`libp2p.New`). -type Option func(cfg *Config) error - -// Apply applies the given options to the config, returning the first error -// encountered (if any). -func (cfg *Config) Apply(opts ...Option) error { - for _, opt := range opts { - if opt == nil { - continue - } - if err := opt(cfg); err != nil { - return err - } - } - return nil -} diff --git a/config/config_js.go b/config/config_js.go new file mode 100644 index 0000000000..662d8d62d5 --- /dev/null +++ b/config/config_js.go @@ -0,0 +1,330 @@ +//go:build js +// +build js + +package config + +import ( + "context" + "errors" + "fmt" + "time" + + "github.com/libp2p/go-libp2p/core/connmgr" + "github.com/libp2p/go-libp2p/core/crypto" + "github.com/libp2p/go-libp2p/core/event" + "github.com/libp2p/go-libp2p/core/host" + "github.com/libp2p/go-libp2p/core/metrics" + "github.com/libp2p/go-libp2p/core/network" + "github.com/libp2p/go-libp2p/core/peer" + "github.com/libp2p/go-libp2p/core/peerstore" + "github.com/libp2p/go-libp2p/core/pnet" + "github.com/libp2p/go-libp2p/core/routing" + "github.com/libp2p/go-libp2p/core/sec" + "github.com/libp2p/go-libp2p/core/sec/insecure" + "github.com/libp2p/go-libp2p/core/transport" + "github.com/libp2p/go-libp2p/p2p/host/autorelay" + bhost "github.com/libp2p/go-libp2p/p2p/host/basic" + "github.com/libp2p/go-libp2p/p2p/host/eventbus" + rcmgr "github.com/libp2p/go-libp2p/p2p/host/resource-manager" + routed "github.com/libp2p/go-libp2p/p2p/host/routed" + "github.com/libp2p/go-libp2p/p2p/net/swarm" + tptu "github.com/libp2p/go-libp2p/p2p/net/upgrader" + circuitv2 "github.com/libp2p/go-libp2p/p2p/protocol/circuitv2/client" + relayv2 "github.com/libp2p/go-libp2p/p2p/protocol/circuitv2/relay" + "github.com/libp2p/go-libp2p/p2p/protocol/holepunch" + "github.com/libp2p/go-libp2p/p2p/protocol/identify" + ma "github.com/multiformats/go-multiaddr" + "github.com/prometheus/client_golang/prometheus" + "go.uber.org/fx" + "go.uber.org/fx/fxevent" +) + +// Config describes a set of settings for a libp2p node +// +// This is *not* a stable interface. Use the options defined in the root +// package. +type Config struct { + // UserAgent is the identifier this node will send to other peers when + // identifying itself, e.g. via the identify protocol. + // + // Set it via the UserAgent option function. + UserAgent string + + // ProtocolVersion is the protocol version that identifies the family + // of protocols used by the peer in the Identify protocol. It is set + // using the [ProtocolVersion] option. + ProtocolVersion string + + PeerKey crypto.PrivKey + + Transports []fx.Option + Muxers []tptu.StreamMuxer + SecurityTransports []Security + Insecure bool + PSK pnet.PSK + + DialTimeout time.Duration + + RelayCustom bool + Relay bool // should the relay transport be used + + EnableRelayService bool // should we run a circuitv2 relay (if publicly reachable) + RelayServiceOpts []relayv2.Option + + ListenAddrs []ma.Multiaddr + AddrsFactory bhost.AddrsFactory + ConnectionGater connmgr.ConnectionGater + + ConnManager connmgr.ConnManager + ResourceManager network.ResourceManager + + NATManager NATManagerC + Peerstore peerstore.Peerstore + Reporter metrics.Reporter + + MultiaddrResolver network.MultiaddrDNSResolver + + DisablePing bool + + Routing RoutingC + + EnableAutoRelay bool + AutoRelayOpts []autorelay.Option + AutoNATConfig + + EnableHolePunching bool + HolePunchingOptions []holepunch.Option + + DisableMetrics bool + PrometheusRegisterer prometheus.Registerer + + DialRanker network.DialRanker + + SwarmOpts []swarm.Option + + DisableIdentifyAddressDiscovery bool + + EnableAutoNATv2 bool + + UDPBlackHoleSuccessCounter *swarm.BlackHoleSuccessCounter + CustomUDPBlackHoleSuccessCounter bool + IPv6BlackHoleSuccessCounter *swarm.BlackHoleSuccessCounter + CustomIPv6BlackHoleSuccessCounter bool + + UserFxOptions []fx.Option + + ShareTCPListener bool +} + +func (cfg *Config) addTransports() ([]fx.Option, error) { + fxopts := []fx.Option{ + fx.WithLogger(func() fxevent.Logger { return getFXLogger() }), + fx.Provide(fx.Annotate(tptu.New, fx.ParamTags(`name:"security"`))), + fx.Supply(cfg.Muxers), + fx.Provide(func() connmgr.ConnectionGater { return cfg.ConnectionGater }), + fx.Provide(func() pnet.PSK { return cfg.PSK }), + fx.Provide(func() network.ResourceManager { return cfg.ResourceManager }), + } + fxopts = append(fxopts, cfg.Transports...) + if cfg.Insecure { + fxopts = append(fxopts, + fx.Provide( + fx.Annotate( + func(id peer.ID, priv crypto.PrivKey) []sec.SecureTransport { + return []sec.SecureTransport{insecure.NewWithIdentity(insecure.ID, id, priv)} + }, + fx.ResultTags(`name:"security"`), + ), + ), + ) + } else { + // fx groups are unordered, but we need to preserve the order of the security transports + // First of all, we construct the security transports that are needed, + // and save them to a group call security_unordered. + for _, s := range cfg.SecurityTransports { + fxName := fmt.Sprintf(`name:"security_%s"`, s.ID) + fxopts = append(fxopts, fx.Supply(fx.Annotate(s.ID, fx.ResultTags(fxName)))) + fxopts = append(fxopts, + fx.Provide(fx.Annotate( + s.Constructor, + fx.ParamTags(fxName), + fx.As(new(sec.SecureTransport)), + fx.ResultTags(`group:"security_unordered"`), + )), + ) + } + // Then we consume the group security_unordered, and order them by the user's preference. + fxopts = append(fxopts, fx.Provide( + fx.Annotate( + func(secs []sec.SecureTransport) ([]sec.SecureTransport, error) { + if len(secs) != len(cfg.SecurityTransports) { + return nil, errors.New("inconsistent length for security transports") + } + t := make([]sec.SecureTransport, 0, len(secs)) + for _, s := range cfg.SecurityTransports { + for _, st := range secs { + if s.ID != st.ID() { + continue + } + t = append(t, st) + } + } + return t, nil + }, + fx.ParamTags(`group:"security_unordered"`), + fx.ResultTags(`name:"security"`), + ))) + } + + fxopts = append(fxopts, fx.Provide(PrivKeyToStatelessResetKey)) + fxopts = append(fxopts, fx.Provide(PrivKeyToTokenGeneratorKey)) + + fxopts = append(fxopts, fx.Invoke( + fx.Annotate( + func(swrm *swarm.Swarm, tpts []transport.Transport) error { + for _, t := range tpts { + if err := swrm.AddTransport(t); err != nil { + return err + } + } + return nil + }, + fx.ParamTags("", `group:"transport"`), + )), + ) + if cfg.Relay { + fxopts = append(fxopts, fx.Invoke(circuitv2.AddTransport)) + } + return fxopts, nil +} + +// NewNode constructs a new libp2p Host from the Config. +// +// This function consumes the config. Do not reuse it (really!). +func (cfg *Config) NewNode() (host.Host, error) { + + validateErr := cfg.validate() + if validateErr != nil { + if cfg.ResourceManager != nil { + cfg.ResourceManager.Close() + } + if cfg.ConnManager != nil { + cfg.ConnManager.Close() + } + if cfg.Peerstore != nil { + cfg.Peerstore.Close() + } + + return nil, validateErr + } + + if !cfg.DisableMetrics { + rcmgr.MustRegisterWith(cfg.PrometheusRegisterer) + } + + fxopts := []fx.Option{ + fx.Provide(func() event.Bus { + return eventbus.NewBus(eventbus.WithMetricsTracer(eventbus.NewMetricsTracer(eventbus.WithRegisterer(cfg.PrometheusRegisterer)))) + }), + fx.Provide(func() crypto.PrivKey { + return cfg.PeerKey + }), + + fx.Provide(cfg.newBasicHost), + fx.Provide(func(bh *bhost.BasicHost) identify.IDService { + return bh.IDService() + }), + fx.Provide(func(bh *bhost.BasicHost) host.Host { + return bh + }), + fx.Provide(func(h *swarm.Swarm) peer.ID { return h.LocalPeer() }), + } + transportOpts, err := cfg.addTransports() + if err != nil { + return nil, err + } + fxopts = append(fxopts, transportOpts...) + + // Configure routing + if cfg.Routing != nil { + fxopts = append(fxopts, + fx.Provide(cfg.Routing), + fx.Provide(func(h host.Host, router routing.PeerRouting) *routed.RoutedHost { + return routed.Wrap(h, router) + }), + ) + } + + // enable autorelay + fxopts = append(fxopts, + fx.Invoke(func(h *bhost.BasicHost, lifecycle fx.Lifecycle) error { + if cfg.EnableAutoRelay { + if !cfg.DisableMetrics { + mt := autorelay.WithMetricsTracer( + autorelay.NewMetricsTracer(autorelay.WithRegisterer(cfg.PrometheusRegisterer))) + mtOpts := []autorelay.Option{mt} + cfg.AutoRelayOpts = append(mtOpts, cfg.AutoRelayOpts...) + } + + ar, err := autorelay.NewAutoRelay(h, cfg.AutoRelayOpts...) + if err != nil { + return err + } + lifecycle.Append(fx.StartStopHook(ar.Start, ar.Close)) + return nil + } + return nil + }), + ) + + fxopts = append(fxopts, + fx.Provide(func(eventBus event.Bus, lifecycle fx.Lifecycle) (*swarm.Swarm, error) { + sw, err := cfg.makeSwarm(eventBus, !cfg.DisableMetrics) + if err != nil { + return nil, err + } + lifecycle.Append(fx.Hook{ + OnStart: func(ctx context.Context) error { + return sw.Listen(cfg.ListenAddrs...) + }, + OnStop: func(ctx context.Context) error { + return sw.Close() + }, + }) + return sw, nil + }), + ) + + var bh *bhost.BasicHost + fxopts = append(fxopts, fx.Invoke(func(bho *bhost.BasicHost) { bh = bho })) + fxopts = append(fxopts, fx.Invoke(func(h *bhost.BasicHost, lifecycle fx.Lifecycle) { + lifecycle.Append(fx.StartHook(h.Start)) + })) + + var rh *routed.RoutedHost + if cfg.Routing != nil { + fxopts = append(fxopts, fx.Invoke(func(bho *routed.RoutedHost) { rh = bho })) + } + + fxopts = append(fxopts, cfg.UserFxOptions...) + + app := fx.New(fxopts...) + if err := app.Start(context.Background()); err != nil { + return nil, err + } + + if err := cfg.addAutoNAT(bh); err != nil { + app.Stop(context.Background()) + if cfg.Routing != nil { + rh.Close() + } else { + bh.Close() + } + return nil, err + } + + if cfg.Routing != nil { + return &closableRoutedHost{App: app, RoutedHost: rh}, nil + } + return &closableBasicHost{App: app, BasicHost: bh}, nil +} diff --git a/config/config_shared.go b/config/config_shared.go new file mode 100644 index 0000000000..0256a2314f --- /dev/null +++ b/config/config_shared.go @@ -0,0 +1,360 @@ +package config + +import ( + "context" + "crypto/rand" + "errors" + "fmt" + "slices" + "time" + + "github.com/libp2p/go-libp2p/core/connmgr" + "github.com/libp2p/go-libp2p/core/crypto" + "github.com/libp2p/go-libp2p/core/event" + "github.com/libp2p/go-libp2p/core/host" + "github.com/libp2p/go-libp2p/core/network" + "github.com/libp2p/go-libp2p/core/peer" + "github.com/libp2p/go-libp2p/core/pnet" + "github.com/libp2p/go-libp2p/core/protocol" + "github.com/libp2p/go-libp2p/core/routing" + "github.com/libp2p/go-libp2p/p2p/host/autonat" + bhost "github.com/libp2p/go-libp2p/p2p/host/basic" + blankhost "github.com/libp2p/go-libp2p/p2p/host/blank" + "github.com/libp2p/go-libp2p/p2p/host/eventbus" + "github.com/libp2p/go-libp2p/p2p/host/peerstore/pstoremem" + "github.com/libp2p/go-libp2p/p2p/net/swarm" + + ma "github.com/multiformats/go-multiaddr" + manet "github.com/multiformats/go-multiaddr/net" + "go.uber.org/fx" +) + +// AddrsFactory is a function that takes a set of multiaddrs we're listening on and +// returns the set of multiaddrs we should advertise to the network. +type AddrsFactory = bhost.AddrsFactory + +// NATManagerC is a NATManager constructor. +type NATManagerC func(network.Network) bhost.NATManager + +type RoutingC func(host.Host) (routing.PeerRouting, error) + +// AutoNATConfig defines the AutoNAT behavior for the libp2p host. +type AutoNATConfig struct { + ForceReachability *network.Reachability + EnableService bool + ThrottleGlobalLimit int + ThrottlePeerLimit int + ThrottleInterval time.Duration +} + +type Security struct { + ID protocol.ID + Constructor interface{} +} + +func (cfg *Config) makeSwarm(eventBus event.Bus, enableMetrics bool) (*swarm.Swarm, error) { + if cfg.Peerstore == nil { + return nil, fmt.Errorf("no peerstore specified") + } + + // Check this early. Prevents us from even *starting* without verifying this. + if pnet.ForcePrivateNetwork && len(cfg.PSK) == 0 { + log.Error("tried to create a libp2p node with no Private" + + " Network Protector but usage of Private Networks" + + " is forced by the environment") + // Note: This is *also* checked the upgrader itself, so it'll be + // enforced even *if* you don't use the libp2p constructor. + return nil, pnet.ErrNotInPrivateNetwork + } + + if cfg.PeerKey == nil { + return nil, fmt.Errorf("no peer key specified") + } + + // Obtain Peer ID from public key + pid, err := peer.IDFromPublicKey(cfg.PeerKey.GetPublic()) + if err != nil { + return nil, err + } + + if err := cfg.Peerstore.AddPrivKey(pid, cfg.PeerKey); err != nil { + return nil, err + } + if err := cfg.Peerstore.AddPubKey(pid, cfg.PeerKey.GetPublic()); err != nil { + return nil, err + } + + opts := append(cfg.SwarmOpts, + swarm.WithUDPBlackHoleSuccessCounter(cfg.UDPBlackHoleSuccessCounter), + swarm.WithIPv6BlackHoleSuccessCounter(cfg.IPv6BlackHoleSuccessCounter), + ) + if cfg.Reporter != nil { + opts = append(opts, swarm.WithMetrics(cfg.Reporter)) + } + if cfg.ConnectionGater != nil { + opts = append(opts, swarm.WithConnectionGater(cfg.ConnectionGater)) + } + if cfg.DialTimeout != 0 { + opts = append(opts, swarm.WithDialTimeout(cfg.DialTimeout)) + } + if cfg.ResourceManager != nil { + opts = append(opts, swarm.WithResourceManager(cfg.ResourceManager)) + } + if cfg.MultiaddrResolver != nil { + opts = append(opts, swarm.WithMultiaddrResolver(cfg.MultiaddrResolver)) + } + if cfg.DialRanker != nil { + opts = append(opts, swarm.WithDialRanker(cfg.DialRanker)) + } + + if enableMetrics { + opts = append(opts, + swarm.WithMetricsTracer(swarm.NewMetricsTracer(swarm.WithRegisterer(cfg.PrometheusRegisterer)))) + } + // TODO: Make the swarm implementation configurable. + return swarm.NewSwarm(pid, cfg.Peerstore, eventBus, opts...) +} + +func (cfg *Config) makeAutoNATV2Host() (host.Host, error) { + autonatPrivKey, _, err := crypto.GenerateEd25519Key(rand.Reader) + if err != nil { + return nil, err + } + ps, err := pstoremem.NewPeerstore() + if err != nil { + return nil, err + } + + autoNatCfg := Config{ + Transports: cfg.Transports, + Muxers: cfg.Muxers, + SecurityTransports: cfg.SecurityTransports, + Insecure: cfg.Insecure, + PSK: cfg.PSK, + ConnectionGater: cfg.ConnectionGater, + Reporter: cfg.Reporter, + PeerKey: autonatPrivKey, + Peerstore: ps, + DialRanker: swarm.NoDelayDialRanker, + UDPBlackHoleSuccessCounter: cfg.UDPBlackHoleSuccessCounter, + IPv6BlackHoleSuccessCounter: cfg.IPv6BlackHoleSuccessCounter, + ResourceManager: cfg.ResourceManager, + SwarmOpts: []swarm.Option{ + // Don't update black hole state for failed autonat dials + swarm.WithReadOnlyBlackHoleDetector(), + }, + } + fxopts, err := autoNatCfg.addTransports() + if err != nil { + return nil, err + } + var dialerHost host.Host + fxopts = append(fxopts, + fx.Provide(eventbus.NewBus), + fx.Provide(func(lifecycle fx.Lifecycle, b event.Bus) (*swarm.Swarm, error) { + lifecycle.Append(fx.Hook{ + OnStop: func(context.Context) error { + return ps.Close() + }}) + sw, err := autoNatCfg.makeSwarm(b, false) + return sw, err + }), + fx.Provide(func(sw *swarm.Swarm) *blankhost.BlankHost { + return blankhost.NewBlankHost(sw) + }), + fx.Provide(func(bh *blankhost.BlankHost) host.Host { + return bh + }), + fx.Provide(func() crypto.PrivKey { return autonatPrivKey }), + fx.Provide(func(bh host.Host) peer.ID { return bh.ID() }), + fx.Invoke(func(bh *blankhost.BlankHost) { + dialerHost = bh + }), + ) + app := fx.New(fxopts...) + if err := app.Err(); err != nil { + return nil, err + } + err = app.Start(context.Background()) + if err != nil { + return nil, err + } + go func() { + <-dialerHost.Network().(*swarm.Swarm).Done() + app.Stop(context.Background()) + }() + return dialerHost, nil +} + +func (cfg *Config) newBasicHost(swrm *swarm.Swarm, eventBus event.Bus) (*bhost.BasicHost, error) { + var autonatv2Dialer host.Host + if cfg.EnableAutoNATv2 { + ah, err := cfg.makeAutoNATV2Host() + if err != nil { + return nil, err + } + autonatv2Dialer = ah + } + h, err := bhost.NewHost(swrm, &bhost.HostOpts{ + EventBus: eventBus, + ConnManager: cfg.ConnManager, + AddrsFactory: cfg.AddrsFactory, + NATManager: cfg.NATManager, + EnablePing: !cfg.DisablePing, + UserAgent: cfg.UserAgent, + ProtocolVersion: cfg.ProtocolVersion, + EnableHolePunching: cfg.EnableHolePunching, + HolePunchingOptions: cfg.HolePunchingOptions, + EnableRelayService: cfg.EnableRelayService, + RelayServiceOpts: cfg.RelayServiceOpts, + EnableMetrics: !cfg.DisableMetrics, + PrometheusRegisterer: cfg.PrometheusRegisterer, + DisableIdentifyAddressDiscovery: cfg.DisableIdentifyAddressDiscovery, + EnableAutoNATv2: cfg.EnableAutoNATv2, + AutoNATv2Dialer: autonatv2Dialer, + }) + if err != nil { + return nil, err + } + return h, nil +} + +func (cfg *Config) validate() error { + if cfg.EnableAutoRelay && !cfg.Relay { + return fmt.Errorf("cannot enable autorelay; relay is not enabled") + } + // If possible check that the resource manager conn limit is higher than the + // limit set in the conn manager. + if l, ok := cfg.ResourceManager.(connmgr.GetConnLimiter); ok { + err := cfg.ConnManager.CheckLimit(l) + if err != nil { + log.Warn(fmt.Sprintf("rcmgr limit conflicts with connmgr limit: %v", err)) + } + } + + if len(cfg.PSK) > 0 && cfg.ShareTCPListener { + return errors.New("cannot use shared TCP listener with PSK") + } + + return nil +} + +func (cfg *Config) addAutoNAT(h *bhost.BasicHost) error { + // Only use public addresses for autonat + addrFunc := func() []ma.Multiaddr { + return slices.DeleteFunc(h.AllAddrs(), func(a ma.Multiaddr) bool { return !manet.IsPublicAddr(a) }) + } + if cfg.AddrsFactory != nil { + addrFunc = func() []ma.Multiaddr { + return slices.DeleteFunc( + slices.Clone(cfg.AddrsFactory(h.AllAddrs())), + func(a ma.Multiaddr) bool { return !manet.IsPublicAddr(a) }) + } + } + autonatOpts := []autonat.Option{ + autonat.UsingAddresses(addrFunc), + } + if !cfg.DisableMetrics { + autonatOpts = append(autonatOpts, autonat.WithMetricsTracer( + autonat.NewMetricsTracer(autonat.WithRegisterer(cfg.PrometheusRegisterer)), + )) + } + if cfg.AutoNATConfig.ThrottleInterval != 0 { + autonatOpts = append(autonatOpts, + autonat.WithThrottling(cfg.AutoNATConfig.ThrottleGlobalLimit, cfg.AutoNATConfig.ThrottleInterval), + autonat.WithPeerThrottling(cfg.AutoNATConfig.ThrottlePeerLimit)) + } + if cfg.AutoNATConfig.EnableService { + autonatPrivKey, _, err := crypto.GenerateEd25519Key(rand.Reader) + if err != nil { + return err + } + ps, err := pstoremem.NewPeerstore() + if err != nil { + return err + } + + // Pull out the pieces of the config that we _actually_ care about. + // Specifically, don't set up things like listeners, identify, etc. + autoNatCfg := Config{ + Transports: cfg.Transports, + Muxers: cfg.Muxers, + SecurityTransports: cfg.SecurityTransports, + Insecure: cfg.Insecure, + PSK: cfg.PSK, + ConnectionGater: cfg.ConnectionGater, + Reporter: cfg.Reporter, + PeerKey: autonatPrivKey, + Peerstore: ps, + DialRanker: swarm.NoDelayDialRanker, + ResourceManager: cfg.ResourceManager, + SwarmOpts: []swarm.Option{ + swarm.WithUDPBlackHoleSuccessCounter(nil), + swarm.WithIPv6BlackHoleSuccessCounter(nil), + }, + } + + fxopts, err := autoNatCfg.addTransports() + if err != nil { + return err + } + var dialer *swarm.Swarm + + fxopts = append(fxopts, + fx.Provide(eventbus.NewBus), + fx.Provide(func(lifecycle fx.Lifecycle, b event.Bus) (*swarm.Swarm, error) { + lifecycle.Append(fx.Hook{ + OnStop: func(context.Context) error { + return ps.Close() + }}) + var err error + dialer, err = autoNatCfg.makeSwarm(b, false) + return dialer, err + + }), + fx.Provide(func(s *swarm.Swarm) peer.ID { return s.LocalPeer() }), + fx.Provide(func() crypto.PrivKey { return autonatPrivKey }), + ) + app := fx.New(fxopts...) + if err := app.Err(); err != nil { + return err + } + err = app.Start(context.Background()) + if err != nil { + return err + } + go func() { + <-dialer.Done() // The swarm used for autonat has closed, we can cleanup now + app.Stop(context.Background()) + }() + autonatOpts = append(autonatOpts, autonat.EnableService(dialer)) + } + if cfg.AutoNATConfig.ForceReachability != nil { + autonatOpts = append(autonatOpts, autonat.WithReachability(*cfg.AutoNATConfig.ForceReachability)) + } + + autonat, err := autonat.New(h, autonatOpts...) + if err != nil { + return fmt.Errorf("autonat init failed: %w", err) + } + h.SetAutoNat(autonat) + return nil +} + +// Option is a libp2p config option that can be given to the libp2p constructor +// (`libp2p.New`). +type Option func(cfg *Config) error + +// Apply applies the given options to the config, returning the first error +// encountered (if any). +func (cfg *Config) Apply(opts ...Option) error { + for _, opt := range opts { + if opt == nil { + continue + } + if err := opt(cfg); err != nil { + return err + } + } + return nil +} diff --git a/defaults.go b/defaults.go index 31de6f025d..e7ad90049c 100644 --- a/defaults.go +++ b/defaults.go @@ -1,16 +1,9 @@ -package libp2p +//go:build !js +// +build !js -// This file contains all the default configuration options. +package libp2p import ( - "crypto/rand" - - "github.com/libp2p/go-libp2p/core/crypto" - "github.com/libp2p/go-libp2p/p2p/host/peerstore/pstoremem" - rcmgr "github.com/libp2p/go-libp2p/p2p/host/resource-manager" - "github.com/libp2p/go-libp2p/p2p/muxer/yamux" - "github.com/libp2p/go-libp2p/p2p/net/connmgr" - "github.com/libp2p/go-libp2p/p2p/net/swarm" "github.com/libp2p/go-libp2p/p2p/security/noise" tls "github.com/libp2p/go-libp2p/p2p/security/tls" quic "github.com/libp2p/go-libp2p/p2p/transport/quic" @@ -18,30 +11,8 @@ import ( libp2pwebrtc "github.com/libp2p/go-libp2p/p2p/transport/webrtc" ws "github.com/libp2p/go-libp2p/p2p/transport/websocket" webtransport "github.com/libp2p/go-libp2p/p2p/transport/webtransport" - "github.com/prometheus/client_golang/prometheus" - - "github.com/multiformats/go-multiaddr" -) - -// DefaultSecurity is the default security option. -// -// Useful when you want to extend, but not replace, the supported transport -// security protocols. -var DefaultSecurity = ChainOptions( - Security(tls.ID, tls.New), - Security(noise.ID, noise.New), ) -// DefaultMuxers configures libp2p to use the stream connection multiplexers. -// -// Use this option when you want to *extend* the set of multiplexers used by -// libp2p instead of replacing them. -var DefaultMuxers = Muxer(yamux.ID, yamux.DefaultTransport) - -// DefaultTransports are the default libp2p transports. -// -// Use this option when you want to *extend* the set of transports used by -// libp2p instead of replacing them. var DefaultTransports = ChainOptions( Transport(tcp.NewTCPTransport), Transport(quic.NewTransport), @@ -50,188 +21,12 @@ var DefaultTransports = ChainOptions( Transport(libp2pwebrtc.New), ) -// DefaultPrivateTransports are the default libp2p transports when a PSK is supplied. -// -// Use this option when you want to *extend* the set of transports used by -// libp2p instead of replacing them. var DefaultPrivateTransports = ChainOptions( Transport(tcp.NewTCPTransport), Transport(ws.New), ) -// DefaultPeerstore configures libp2p to use the default peerstore. -var DefaultPeerstore Option = func(cfg *Config) error { - ps, err := pstoremem.NewPeerstore() - if err != nil { - return err - } - return cfg.Apply(Peerstore(ps)) -} - -// RandomIdentity generates a random identity. (default behaviour) -var RandomIdentity = func(cfg *Config) error { - priv, _, err := crypto.GenerateEd25519Key(rand.Reader) - if err != nil { - return err - } - return cfg.Apply(Identity(priv)) -} - -// DefaultListenAddrs configures libp2p to use default listen address. -var DefaultListenAddrs = func(cfg *Config) error { - addrs := []string{ - "/ip4/0.0.0.0/tcp/0", - "/ip4/0.0.0.0/udp/0/quic-v1", - "/ip4/0.0.0.0/udp/0/quic-v1/webtransport", - "/ip4/0.0.0.0/udp/0/webrtc-direct", - "/ip6/::/tcp/0", - "/ip6/::/udp/0/quic-v1", - "/ip6/::/udp/0/quic-v1/webtransport", - "/ip6/::/udp/0/webrtc-direct", - } - listenAddrs := make([]multiaddr.Multiaddr, 0, len(addrs)) - for _, s := range addrs { - addr, err := multiaddr.NewMultiaddr(s) - if err != nil { - return err - } - listenAddrs = append(listenAddrs, addr) - } - return cfg.Apply(ListenAddrs(listenAddrs...)) -} - -// DefaultEnableRelay enables relay dialing and listening by default. -var DefaultEnableRelay = func(cfg *Config) error { - return cfg.Apply(EnableRelay()) -} - -var DefaultResourceManager = func(cfg *Config) error { - // Default memory limit: 1/8th of total memory, minimum 128MB, maximum 1GB - limits := rcmgr.DefaultLimits - SetDefaultServiceLimits(&limits) - mgr, err := rcmgr.NewResourceManager(rcmgr.NewFixedLimiter(limits.AutoScale())) - if err != nil { - return err - } - - return cfg.Apply(ResourceManager(mgr)) -} - -// DefaultConnectionManager creates a default connection manager -var DefaultConnectionManager = func(cfg *Config) error { - mgr, err := connmgr.NewConnManager(160, 192) - if err != nil { - return err - } - - return cfg.Apply(ConnectionManager(mgr)) -} - -// DefaultPrometheusRegisterer configures libp2p to use the default registerer -var DefaultPrometheusRegisterer = func(cfg *Config) error { - return cfg.Apply(PrometheusRegisterer(prometheus.DefaultRegisterer)) -} - -var defaultUDPBlackHoleDetector = func(cfg *Config) error { - // A black hole is a binary property. On a network if UDP dials are blocked, all dials will - // fail. So a low success rate of 5 out 100 dials is good enough. - return cfg.Apply(UDPBlackHoleSuccessCounter(&swarm.BlackHoleSuccessCounter{N: 100, MinSuccesses: 5, Name: "UDP"})) -} - -var defaultIPv6BlackHoleDetector = func(cfg *Config) error { - // A black hole is a binary property. On a network if there is no IPv6 connectivity, all - // dials will fail. So a low success rate of 5 out 100 dials is good enough. - return cfg.Apply(IPv6BlackHoleSuccessCounter(&swarm.BlackHoleSuccessCounter{N: 100, MinSuccesses: 5, Name: "IPv6"})) -} - -// Complete list of default options and when to fallback on them. -// -// Please *DON'T* specify default options any other way. Putting this all here -// makes tracking defaults *much* easier. -var defaults = []struct { - fallback func(cfg *Config) bool - opt Option -}{ - { - fallback: func(cfg *Config) bool { return cfg.Transports == nil && cfg.ListenAddrs == nil }, - opt: DefaultListenAddrs, - }, - { - fallback: func(cfg *Config) bool { return cfg.Transports == nil && cfg.PSK == nil }, - opt: DefaultTransports, - }, - { - fallback: func(cfg *Config) bool { return cfg.Transports == nil && cfg.PSK != nil }, - opt: DefaultPrivateTransports, - }, - { - fallback: func(cfg *Config) bool { return cfg.Muxers == nil }, - opt: DefaultMuxers, - }, - { - fallback: func(cfg *Config) bool { return !cfg.Insecure && cfg.SecurityTransports == nil }, - opt: DefaultSecurity, - }, - { - fallback: func(cfg *Config) bool { return cfg.PeerKey == nil }, - opt: RandomIdentity, - }, - { - fallback: func(cfg *Config) bool { return cfg.Peerstore == nil }, - opt: DefaultPeerstore, - }, - { - fallback: func(cfg *Config) bool { return !cfg.RelayCustom }, - opt: DefaultEnableRelay, - }, - { - fallback: func(cfg *Config) bool { return cfg.ResourceManager == nil }, - opt: DefaultResourceManager, - }, - { - fallback: func(cfg *Config) bool { return cfg.ConnManager == nil }, - opt: DefaultConnectionManager, - }, - { - fallback: func(cfg *Config) bool { return !cfg.DisableMetrics && cfg.PrometheusRegisterer == nil }, - opt: DefaultPrometheusRegisterer, - }, - { - fallback: func(cfg *Config) bool { - return !cfg.CustomUDPBlackHoleSuccessCounter && cfg.UDPBlackHoleSuccessCounter == nil - }, - opt: defaultUDPBlackHoleDetector, - }, - { - fallback: func(cfg *Config) bool { - return !cfg.CustomIPv6BlackHoleSuccessCounter && cfg.IPv6BlackHoleSuccessCounter == nil - }, - opt: defaultIPv6BlackHoleDetector, - }, -} - -// Defaults configures libp2p to use the default options. Can be combined with -// other options to *extend* the default options. -var Defaults Option = func(cfg *Config) error { - for _, def := range defaults { - if err := cfg.Apply(def.opt); err != nil { - return err - } - } - return nil -} - -// FallbackDefaults applies default options to the libp2p node if and only if no -// other relevant options have been applied. will be appended to the options -// passed into New. -var FallbackDefaults Option = func(cfg *Config) error { - for _, def := range defaults { - if !def.fallback(cfg) { - continue - } - if err := cfg.Apply(def.opt); err != nil { - return err - } - } - return nil -} +var DefaultSecurity = ChainOptions( + Security(tls.ID, tls.New), + Security(noise.ID, noise.New), +) diff --git a/defaults_js.go b/defaults_js.go new file mode 100644 index 0000000000..ae46b30189 --- /dev/null +++ b/defaults_js.go @@ -0,0 +1,14 @@ +//go:build js +// +build js + +package libp2p + +import "github.com/libp2p/go-libp2p/p2p/security/noise" + +// Only WebSocket and WebTransport are supported in the browser. +var DefaultTransports = ChainOptions() +var DefaultPrivateTransports = ChainOptions() + +var DefaultSecurity = ChainOptions( + Security(noise.ID, noise.New), +) diff --git a/defaults_shared.go b/defaults_shared.go new file mode 100644 index 0000000000..ad055814a2 --- /dev/null +++ b/defaults_shared.go @@ -0,0 +1,215 @@ +package libp2p + +// This file contains all the default configuration options. + +import ( + "crypto/rand" + + "github.com/libp2p/go-libp2p/core/crypto" + "github.com/libp2p/go-libp2p/p2p/host/peerstore/pstoremem" + rcmgr "github.com/libp2p/go-libp2p/p2p/host/resource-manager" + "github.com/libp2p/go-libp2p/p2p/muxer/yamux" + "github.com/libp2p/go-libp2p/p2p/net/connmgr" + "github.com/libp2p/go-libp2p/p2p/net/swarm" + "github.com/prometheus/client_golang/prometheus" + + "github.com/multiformats/go-multiaddr" +) + +// DefaultSecurity is the default security option. +// +// Useful when you want to extend, but not replace, the supported transport +// security protocols. + +// DefaultMuxers configures libp2p to use the stream connection multiplexers. +// +// Use this option when you want to *extend* the set of multiplexers used by +// libp2p instead of replacing them. +var DefaultMuxers = Muxer(yamux.ID, yamux.DefaultTransport) + +// DefaultTransports are the default libp2p transports. +// +// Use this option when you want to *extend* the set of transports used by +// libp2p instead of replacing them. + +// DefaultPrivateTransports are the default libp2p transports when a PSK is supplied. +// +// Use this option when you want to *extend* the set of transports used by +// libp2p instead of replacing them. + +// DefaultPeerstore configures libp2p to use the default peerstore. +var DefaultPeerstore Option = func(cfg *Config) error { + ps, err := pstoremem.NewPeerstore() + if err != nil { + return err + } + return cfg.Apply(Peerstore(ps)) +} + +// RandomIdentity generates a random identity. (default behaviour) +var RandomIdentity = func(cfg *Config) error { + priv, _, err := crypto.GenerateEd25519Key(rand.Reader) + if err != nil { + return err + } + return cfg.Apply(Identity(priv)) +} + +// DefaultListenAddrs configures libp2p to use default listen address. +var DefaultListenAddrs = func(cfg *Config) error { + addrs := []string{ + "/ip4/0.0.0.0/tcp/0", + "/ip4/0.0.0.0/udp/0/quic-v1", + "/ip4/0.0.0.0/udp/0/quic-v1/webtransport", + "/ip4/0.0.0.0/udp/0/webrtc-direct", + "/ip6/::/tcp/0", + "/ip6/::/udp/0/quic-v1", + "/ip6/::/udp/0/quic-v1/webtransport", + "/ip6/::/udp/0/webrtc-direct", + } + listenAddrs := make([]multiaddr.Multiaddr, 0, len(addrs)) + for _, s := range addrs { + addr, err := multiaddr.NewMultiaddr(s) + if err != nil { + return err + } + listenAddrs = append(listenAddrs, addr) + } + return cfg.Apply(ListenAddrs(listenAddrs...)) +} + +// DefaultEnableRelay enables relay dialing and listening by default. +var DefaultEnableRelay = func(cfg *Config) error { + return cfg.Apply(EnableRelay()) +} + +var DefaultResourceManager = func(cfg *Config) error { + // Default memory limit: 1/8th of total memory, minimum 128MB, maximum 1GB + limits := rcmgr.DefaultLimits + SetDefaultServiceLimits(&limits) + mgr, err := rcmgr.NewResourceManager(rcmgr.NewFixedLimiter(limits.AutoScale())) + if err != nil { + return err + } + + return cfg.Apply(ResourceManager(mgr)) +} + +// DefaultConnectionManager creates a default connection manager +var DefaultConnectionManager = func(cfg *Config) error { + mgr, err := connmgr.NewConnManager(160, 192) + if err != nil { + return err + } + + return cfg.Apply(ConnectionManager(mgr)) +} + +// DefaultPrometheusRegisterer configures libp2p to use the default registerer +var DefaultPrometheusRegisterer = func(cfg *Config) error { + return cfg.Apply(PrometheusRegisterer(prometheus.DefaultRegisterer)) +} + +var defaultUDPBlackHoleDetector = func(cfg *Config) error { + // A black hole is a binary property. On a network if UDP dials are blocked, all dials will + // fail. So a low success rate of 5 out 100 dials is good enough. + return cfg.Apply(UDPBlackHoleSuccessCounter(&swarm.BlackHoleSuccessCounter{N: 100, MinSuccesses: 5, Name: "UDP"})) +} + +var defaultIPv6BlackHoleDetector = func(cfg *Config) error { + // A black hole is a binary property. On a network if there is no IPv6 connectivity, all + // dials will fail. So a low success rate of 5 out 100 dials is good enough. + return cfg.Apply(IPv6BlackHoleSuccessCounter(&swarm.BlackHoleSuccessCounter{N: 100, MinSuccesses: 5, Name: "IPv6"})) +} + +// Complete list of default options and when to fallback on them. +// +// Please *DON'T* specify default options any other way. Putting this all here +// makes tracking defaults *much* easier. +var defaults = []struct { + fallback func(cfg *Config) bool + opt Option +}{ + { + fallback: func(cfg *Config) bool { return cfg.Transports == nil && cfg.ListenAddrs == nil }, + opt: DefaultListenAddrs, + }, + { + fallback: func(cfg *Config) bool { return cfg.Transports == nil && cfg.PSK == nil }, + opt: DefaultTransports, + }, + { + fallback: func(cfg *Config) bool { return cfg.Transports == nil && cfg.PSK != nil }, + opt: DefaultPrivateTransports, + }, + { + fallback: func(cfg *Config) bool { return cfg.Muxers == nil }, + opt: DefaultMuxers, + }, + { + fallback: func(cfg *Config) bool { return !cfg.Insecure && cfg.SecurityTransports == nil }, + opt: DefaultSecurity, + }, + { + fallback: func(cfg *Config) bool { return cfg.PeerKey == nil }, + opt: RandomIdentity, + }, + { + fallback: func(cfg *Config) bool { return cfg.Peerstore == nil }, + opt: DefaultPeerstore, + }, + { + fallback: func(cfg *Config) bool { return !cfg.RelayCustom }, + opt: DefaultEnableRelay, + }, + { + fallback: func(cfg *Config) bool { return cfg.ResourceManager == nil }, + opt: DefaultResourceManager, + }, + { + fallback: func(cfg *Config) bool { return cfg.ConnManager == nil }, + opt: DefaultConnectionManager, + }, + { + fallback: func(cfg *Config) bool { return !cfg.DisableMetrics && cfg.PrometheusRegisterer == nil }, + opt: DefaultPrometheusRegisterer, + }, + { + fallback: func(cfg *Config) bool { + return !cfg.CustomUDPBlackHoleSuccessCounter && cfg.UDPBlackHoleSuccessCounter == nil + }, + opt: defaultUDPBlackHoleDetector, + }, + { + fallback: func(cfg *Config) bool { + return !cfg.CustomIPv6BlackHoleSuccessCounter && cfg.IPv6BlackHoleSuccessCounter == nil + }, + opt: defaultIPv6BlackHoleDetector, + }, +} + +// Defaults configures libp2p to use the default options. Can be combined with +// other options to *extend* the default options. +var Defaults Option = func(cfg *Config) error { + for _, def := range defaults { + if err := cfg.Apply(def.opt); err != nil { + return err + } + } + return nil +} + +// FallbackDefaults applies default options to the libp2p node if and only if no +// other relevant options have been applied. will be appended to the options +// passed into New. +var FallbackDefaults Option = func(cfg *Config) error { + for _, def := range defaults { + if !def.fallback(cfg) { + continue + } + if err := cfg.Apply(def.opt); err != nil { + return err + } + } + return nil +} diff --git a/examples/go.mod b/examples/go.mod index 3bb1c16970..b1696d8812 100644 --- a/examples/go.mod +++ b/examples/go.mod @@ -6,7 +6,7 @@ require ( github.com/caddyserver/certmagic v0.21.6 github.com/gogo/protobuf v1.3.2 github.com/google/uuid v1.6.0 - github.com/ipfs/go-datastore v0.6.0 + github.com/ipfs/go-datastore v0.8.2 github.com/ipfs/go-log/v2 v2.5.1 github.com/ipshipyard/p2p-forge v0.5.0 github.com/libp2p/go-libp2p v0.41.1 @@ -20,19 +20,14 @@ require ( github.com/beorn7/perks v1.0.1 // indirect github.com/caddyserver/zerossl v0.1.3 // indirect github.com/cespare/xxhash/v2 v2.3.0 // indirect - github.com/containerd/cgroups v1.1.0 // indirect - github.com/coreos/go-systemd/v22 v22.5.0 // indirect github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect github.com/davidlazar/go-crypto v0.0.0-20200604182044-b73af7476f6c // indirect github.com/decred/dcrd/dcrec/secp256k1/v4 v4.4.0 // indirect - github.com/docker/go-units v0.5.0 // indirect - github.com/elastic/gosigar v0.14.3 // indirect github.com/flynn/noise v1.1.0 // indirect github.com/francoispqt/gojay v1.2.13 // indirect github.com/go-logr/logr v1.4.2 // indirect github.com/go-logr/stdr v1.2.2 // indirect github.com/go-task/slim-sprig/v3 v3.0.0 // indirect - github.com/godbus/dbus/v5 v5.1.0 // indirect github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8 // indirect github.com/google/gopacket v1.1.19 // indirect github.com/google/pprof v0.0.0-20250208200701-d0013a598941 // indirect @@ -46,7 +41,6 @@ require ( github.com/ipld/go-ipld-prime v0.21.0 // indirect github.com/jackpal/go-nat-pmp v1.0.2 // indirect github.com/jbenet/go-temp-err-catcher v0.1.0 // indirect - github.com/jbenet/goprocess v0.1.4 // indirect github.com/klauspost/compress v1.18.0 // indirect github.com/klauspost/cpuid/v2 v2.2.10 // indirect github.com/koron/go-ssdp v0.0.5 // indirect @@ -82,7 +76,6 @@ require ( github.com/multiformats/go-varint v0.0.7 // indirect github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect github.com/onsi/ginkgo/v2 v2.22.2 // indirect - github.com/opencontainers/runtime-spec v1.2.0 // indirect github.com/pbnjay/memory v0.0.0-20210728143218-7b4eea64cf58 // indirect github.com/pion/datachannel v1.5.10 // indirect github.com/pion/dtls/v2 v2.2.12 // indirect @@ -93,17 +86,16 @@ require ( github.com/pion/mdns/v2 v2.0.7 // indirect github.com/pion/randutil v0.1.0 // indirect github.com/pion/rtcp v1.2.15 // indirect - github.com/pion/rtp v1.8.11 // indirect + github.com/pion/rtp v1.8.13 // indirect github.com/pion/sctp v1.8.37 // indirect - github.com/pion/sdp/v3 v3.0.10 // indirect + github.com/pion/sdp/v3 v3.0.11 // indirect github.com/pion/srtp/v3 v3.0.4 // indirect github.com/pion/stun v0.6.1 // indirect github.com/pion/stun/v3 v3.0.0 // indirect github.com/pion/transport/v2 v2.2.10 // indirect github.com/pion/transport/v3 v3.0.7 // indirect github.com/pion/turn/v4 v4.0.0 // indirect - github.com/pion/webrtc/v4 v4.0.10 // indirect - github.com/pkg/errors v0.9.1 // indirect + github.com/pion/webrtc/v4 v4.0.14 // indirect github.com/polydawn/refmt v0.89.0 // indirect github.com/prometheus/client_model v0.6.1 // indirect github.com/prometheus/common v0.62.0 // indirect @@ -111,16 +103,15 @@ require ( github.com/quic-go/qpack v0.5.1 // indirect github.com/quic-go/quic-go v0.50.1 // indirect github.com/quic-go/webtransport-go v0.8.1-0.20241018022711-4ac2c9250e66 // indirect - github.com/raulk/go-watchdog v1.3.0 // indirect github.com/spaolacci/murmur3 v1.1.0 // indirect github.com/whyrusleeping/go-keyspace v0.0.0-20160322163242-5b898ac5add1 // indirect github.com/wlynxg/anet v0.0.5 // indirect github.com/zeebo/blake3 v0.2.4 // indirect go.opencensus.io v0.24.0 // indirect go.opentelemetry.io/auto/sdk v1.1.0 // indirect - go.opentelemetry.io/otel v1.33.0 // indirect - go.opentelemetry.io/otel/metric v1.33.0 // indirect - go.opentelemetry.io/otel/trace v1.33.0 // indirect + go.opentelemetry.io/otel v1.34.0 // indirect + go.opentelemetry.io/otel/metric v1.34.0 // indirect + go.opentelemetry.io/otel/trace v1.34.0 // indirect go.uber.org/dig v1.18.0 // indirect go.uber.org/fx v1.23.0 // indirect go.uber.org/mock v0.5.0 // indirect @@ -134,9 +125,11 @@ require ( golang.org/x/sync v0.11.0 // indirect golang.org/x/sys v0.30.0 // indirect golang.org/x/text v0.22.0 // indirect - golang.org/x/time v0.8.0 // indirect + golang.org/x/time v0.11.0 // indirect golang.org/x/tools v0.30.0 // indirect gonum.org/v1/gonum v0.15.1 // indirect google.golang.org/protobuf v1.36.6 // indirect lukechampine.com/blake3 v1.4.0 // indirect ) + +replace github.com/libp2p/go-libp2p => ../ diff --git a/examples/go.sum b/examples/go.sum index 057983ce62..11b4e8e0ba 100644 --- a/examples/go.sum +++ b/examples/go.sum @@ -10,7 +10,6 @@ git.apache.org/thrift.git v0.0.0-20180902110319-2566ecd5d999/go.mod h1:fPE2ZNJGy github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYUyUczH0OGQWaF5ceTx0UBShxjsH6f8oGKYe2c= github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= -github.com/benbjohnson/clock v1.3.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= github.com/benbjohnson/clock v1.3.5 h1:VvXlSJBzZpA/zum6Sj74hxwYI2DIxRWuNIoXAzHZz5o= github.com/benbjohnson/clock v1.3.5/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= @@ -25,18 +24,10 @@ github.com/caddyserver/zerossl v0.1.3/go.mod h1:CxA0acn7oEGO6//4rtrRjYgEoa4MFw/X github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= -github.com/cilium/ebpf v0.2.0/go.mod h1:To2CFviqOWL/M0gIMsvSMlqe7em/l1ALkX1PyjrX2Qs= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= -github.com/containerd/cgroups v0.0.0-20201119153540-4cbc285b3327/go.mod h1:ZJeTFisyysqgcCdecO57Dj79RfL0LNeGiFUqLYQRYLE= -github.com/containerd/cgroups v1.1.0 h1:v8rEWFl6EoqHB+swVNjVoCJE8o3jX7e8nqBGPLaDFBM= -github.com/containerd/cgroups v1.1.0/go.mod h1:6ppBcbh/NOOUU+dMKrykgaBnK9lCIBxHqJDGwsa1mIw= github.com/coreos/go-systemd v0.0.0-20181012123002-c6f51f82210d/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= -github.com/coreos/go-systemd/v22 v22.1.0/go.mod h1:xO0FLkIi5MaZafQlIrOotqXZ90ih+1atmu1JpKERPPk= -github.com/coreos/go-systemd/v22 v22.5.0 h1:RrqgGjYQKalulkV8NGVIfkXQf6YYmOyiJKk8iXXhfZs= -github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= -github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= @@ -47,13 +38,7 @@ github.com/decred/dcrd/crypto/blake256 v1.1.0 h1:zPMNGQCm0g4QTY27fOCorQW7EryeQ/U github.com/decred/dcrd/crypto/blake256 v1.1.0/go.mod h1:2OfgNZ5wDpcsFmHmCK5gZTPcCXqlm2ArzUIkw9czNJo= github.com/decred/dcrd/dcrec/secp256k1/v4 v4.4.0 h1:NMZiJj8QnKe1LgsbDayM4UoHwbvwDRwnI3hwNaAHRnc= github.com/decred/dcrd/dcrec/secp256k1/v4 v4.4.0/go.mod h1:ZXNYxsqcloTdSy/rNShjYzMhyjf0LaoftYK0p+A3h40= -github.com/docker/go-units v0.4.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= -github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4= -github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= -github.com/elastic/gosigar v0.12.0/go.mod h1:iXRIGg2tLnu7LBdpqzyQfGDEidKCfWcCMS0WKyPWoMs= -github.com/elastic/gosigar v0.14.3 h1:xwkKwPia+hSfg9GqrCUKYdId102m9qTJIIr7egmK/uo= -github.com/elastic/gosigar v0.14.3/go.mod h1:iXRIGg2tLnu7LBdpqzyQfGDEidKCfWcCMS0WKyPWoMs= github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= @@ -77,12 +62,7 @@ github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1vB6EwHI= github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8= github.com/go-yaml/yaml v2.1.0+incompatible/go.mod h1:w2MrLa16VYP0jy6N7M5kHaCkaLENm+P+Tv+MfurjSw0= -github.com/godbus/dbus/v5 v5.0.3/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= -github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= -github.com/godbus/dbus/v5 v5.1.0 h1:4KLkAxT3aOY8Li4FRJe/KvhoNFFxo0m6fNuFUO8QJUk= -github.com/godbus/dbus/v5 v5.1.0/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= -github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o= github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= @@ -108,7 +88,6 @@ github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMyw github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= @@ -147,8 +126,8 @@ github.com/ipfs/go-block-format v0.2.0 h1:ZqrkxBA2ICbDRbK8KJs/u0O3dlp6gmAuuXUJNi github.com/ipfs/go-block-format v0.2.0/go.mod h1:+jpL11nFx5A/SPpsoBn6Bzkra/zaArfSmsknbPMYgzM= github.com/ipfs/go-cid v0.5.0 h1:goEKKhaGm0ul11IHA7I6p1GmKz8kEYniqFopaB5Otwg= github.com/ipfs/go-cid v0.5.0/go.mod h1:0L7vmeNXpQpUS9vt+yEARkJ8rOg43DF3iPgn4GIN0mk= -github.com/ipfs/go-datastore v0.6.0 h1:JKyz+Gvz1QEZw0LsX1IBn+JFCJQH4SJVFtM4uWU0Myk= -github.com/ipfs/go-datastore v0.6.0/go.mod h1:rt5M3nNbSO/8q1t4LNkLyUwRs8HupMeN/8O4Vn9YAT8= +github.com/ipfs/go-datastore v0.8.2 h1:Jy3wjqQR6sg/LhyY0NIePZC3Vux19nLtg7dx0TVqr6U= +github.com/ipfs/go-datastore v0.8.2/go.mod h1:W+pI1NsUsz3tcsAACMtfC+IZdnQTnC/7VfPoJBQuts0= github.com/ipfs/go-detect-race v0.0.1 h1:qX/xay2W3E4Q1U7d9lNs1sU9nvguX0a7319XbyQ6cOk= github.com/ipfs/go-detect-race v0.0.1/go.mod h1:8BNT7shDZPo99Q74BpGMK+4D8Mn4j46UU0LZ723meps= github.com/ipfs/go-ipfs-util v0.0.3 h1:2RFdGez6bu2ZlZdI+rWfIdbQb1KudQp3VGwPtdNCmE0= @@ -163,17 +142,13 @@ github.com/ipshipyard/p2p-forge v0.5.0 h1:U1ta2RYkSOLPXNbeCWGT5iv5t5TS1GNDvE1hSu github.com/ipshipyard/p2p-forge v0.5.0/go.mod h1:GNDXM2CR8KRS8mJGw7ARIRVlrG9NH8MdewgNVfIIByA= github.com/jackpal/go-nat-pmp v1.0.2 h1:KzKSgb7qkJvOUTqYl9/Hg/me3pWgBmERKrTGD7BdWus= github.com/jackpal/go-nat-pmp v1.0.2/go.mod h1:QPH045xvCAeXUZOxsnwmrtiCoxIr9eob+4orBN1SBKc= -github.com/jbenet/go-cienv v0.1.0/go.mod h1:TqNnHUmJgXau0nCzC7kXWeotg3J9W34CUv5Djy1+FlA= github.com/jbenet/go-temp-err-catcher v0.1.0 h1:zpb3ZH6wIE8Shj2sKS+khgRvf7T7RABoLk/+KKHggpk= github.com/jbenet/go-temp-err-catcher v0.1.0/go.mod h1:0kJRvmDZXNMIiJirNPEYfhpPwbGVtZVWC34vc5WLsDk= -github.com/jbenet/goprocess v0.1.4 h1:DRGOFReOMqqDNXwW70QkacFW0YN9QnwLV0Vqk+3oU0o= -github.com/jbenet/goprocess v0.1.4/go.mod h1:5yspPrukOVuOLORacaBi858NqyClJPQxYZlqdZVfqY4= github.com/jellevandenhooff/dkim v0.0.0-20150330215556-f50fe3d243e1/go.mod h1:E0B/fFc00Y+Rasa88328GlI/XbtyysCtTHZS8h7IrBU= github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo= github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= -github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00= github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/klauspost/compress v1.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zttxdo= @@ -201,8 +176,6 @@ github.com/libp2p/go-cidranger v1.1.0 h1:ewPN8EZ0dd1LSnrtuwd4709PXVcITVeuwbag38y github.com/libp2p/go-cidranger v1.1.0/go.mod h1:KWZTfSr+r9qEo9OkI9/SIEeAtw+NNoU0dXIXt15Okic= github.com/libp2p/go-flow-metrics v0.2.0 h1:EIZzjmeOE6c8Dav0sNv35vhZxATIXWZg6j/C08XmmDw= github.com/libp2p/go-flow-metrics v0.2.0/go.mod h1:st3qqfu8+pMfh+9Mzqb2GTiwrAGjIPszEjZmtksN8Jc= -github.com/libp2p/go-libp2p v0.41.1 h1:8ecNQVT5ev/jqALTvisSJeVNvXYJyK4NhQx1nNRXQZE= -github.com/libp2p/go-libp2p v0.41.1/go.mod h1:DcGTovJzQl/I7HMrby5ZRjeD0kQkGiy+9w6aEkSZpRI= github.com/libp2p/go-libp2p-asn-util v0.4.1 h1:xqL7++IKD9TBFMgnLPZR6/6iYhawHKHl950SO9L6n94= github.com/libp2p/go-libp2p-asn-util v0.4.1/go.mod h1:d/NI6XZ9qxw67b4e+NgpQexCIiFYJjErASrYW4PFDN8= github.com/libp2p/go-libp2p-kad-dht v0.28.1 h1:DVTfzG8Ybn88g9RycIq47evWCRss5f0Wm8iWtpwyHso= @@ -284,9 +257,6 @@ github.com/onsi/ginkgo/v2 v2.22.2 h1:/3X8Panh8/WwhU/3Ssa6rCKqPLuAkVY2I0RoyDLySlU github.com/onsi/ginkgo/v2 v2.22.2/go.mod h1:oeMosUL+8LtarXBHu/c0bx2D/K9zyQ6uX3cTyztHwsk= github.com/onsi/gomega v1.36.2 h1:koNYke6TVk6ZmnyHrCXba/T/MoLBXFjeC1PtvYgw0A8= github.com/onsi/gomega v1.36.2/go.mod h1:DdwyADRjrc825LhMEkD76cHR5+pUnjhUN8GlHlRPHzY= -github.com/opencontainers/runtime-spec v1.0.2/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= -github.com/opencontainers/runtime-spec v1.2.0 h1:z97+pHb3uELt/yiAWD691HNHQIF07bE7dzrbT927iTk= -github.com/opencontainers/runtime-spec v1.2.0/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= github.com/openzipkin/zipkin-go v0.1.1/go.mod h1:NtoC/o8u3JlF1lSlyPNswIbeQH9bJTmOf0Erfk+hxe8= github.com/pbnjay/memory v0.0.0-20210728143218-7b4eea64cf58 h1:onHthvaw9LFnH4t2DcNVpwGmV9E1BkGknEliJkfwQj0= github.com/pbnjay/memory v0.0.0-20210728143218-7b4eea64cf58/go.mod h1:DXv8WO4yhMYhSNPKjeNKa5WY9YCIEBRbNzFFPJbWO6Y= @@ -310,12 +280,12 @@ github.com/pion/randutil v0.1.0 h1:CFG1UdESneORglEsnimhUjf33Rwjubwj6xfiOXBa3mA= github.com/pion/randutil v0.1.0/go.mod h1:XcJrSMMbbMRhASFVOlj/5hQial/Y8oH/HVo7TBZq+j8= github.com/pion/rtcp v1.2.15 h1:LZQi2JbdipLOj4eBjK4wlVoQWfrZbh3Q6eHtWtJBZBo= github.com/pion/rtcp v1.2.15/go.mod h1:jlGuAjHMEXwMUHK78RgX0UmEJFV4zUKOFHR7OP+D3D0= -github.com/pion/rtp v1.8.11 h1:17xjnY5WO5hgO6SD3/NTIUPvSFw/PbLsIJyz1r1yNIk= -github.com/pion/rtp v1.8.11/go.mod h1:8uMBJj32Pa1wwx8Fuv/AsFhn8jsgw+3rUC2PfoBZ8p4= +github.com/pion/rtp v1.8.13 h1:8uSUPpjSL4OlwZI8Ygqu7+h2p9NPFB+yAZ461Xn5sNg= +github.com/pion/rtp v1.8.13/go.mod h1:8uMBJj32Pa1wwx8Fuv/AsFhn8jsgw+3rUC2PfoBZ8p4= github.com/pion/sctp v1.8.37 h1:ZDmGPtRPX9mKCiVXtMbTWybFw3z/hVKAZgU81wcOrqs= github.com/pion/sctp v1.8.37/go.mod h1:cNiLdchXra8fHQwmIoqw0MbLLMs+f7uQ+dGMG2gWebE= -github.com/pion/sdp/v3 v3.0.10 h1:6MChLE/1xYB+CjumMw+gZ9ufp2DPApuVSnDT8t5MIgA= -github.com/pion/sdp/v3 v3.0.10/go.mod h1:88GMahN5xnScv1hIMTqLdu/cOcUkj6a9ytbncwMCq2E= +github.com/pion/sdp/v3 v3.0.11 h1:VhgVSopdsBKwhCFoyyPmT1fKMeV9nLMrEKxNOdy3IVI= +github.com/pion/sdp/v3 v3.0.11/go.mod h1:88GMahN5xnScv1hIMTqLdu/cOcUkj6a9ytbncwMCq2E= github.com/pion/srtp/v3 v3.0.4 h1:2Z6vDVxzrX3UHEgrUyIGM4rRouoC7v+NiF1IHtp9B5M= github.com/pion/srtp/v3 v3.0.4/go.mod h1:1Jx3FwDoxpRaTh1oRV8A/6G1BnFL+QI82eK4ms8EEJQ= github.com/pion/stun v0.6.1 h1:8lp6YejULeHBF8NmV8e2787BogQhduZugh5PdhDyyN4= @@ -330,11 +300,9 @@ github.com/pion/transport/v3 v3.0.7 h1:iRbMH05BzSNwhILHoBoAPxoB9xQgOaJk+591KC9P1 github.com/pion/transport/v3 v3.0.7/go.mod h1:YleKiTZ4vqNxVwh77Z0zytYi7rXHl7j6uPLGhhz9rwo= github.com/pion/turn/v4 v4.0.0 h1:qxplo3Rxa9Yg1xXDxxH8xaqcyGUtbHYw4QSCvmFWvhM= github.com/pion/turn/v4 v4.0.0/go.mod h1:MuPDkm15nYSklKpN8vWJ9W2M0PlyQZqYt1McGuxG7mA= -github.com/pion/webrtc/v4 v4.0.10 h1:Hq/JLjhqLxi+NmCtE8lnRPDr8H4LcNvwg8OxVcdv56Q= -github.com/pion/webrtc/v4 v4.0.10/go.mod h1:ViHLVaNpiuvaH8pdiuQxuA9awuE6KVzAXx3vVWilOck= +github.com/pion/webrtc/v4 v4.0.14 h1:nyds/sFRR+HvmWoBa6wrL46sSfpArE0qR883MBW96lg= +github.com/pion/webrtc/v4 v4.0.14/go.mod h1:R3+qTnQTS03UzwDarYecgioNf7DYgTsldxnCXB821Kk= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= -github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= -github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= @@ -359,8 +327,6 @@ github.com/quic-go/quic-go v0.50.1 h1:unsgjFIUqW8a2oopkY7YNONpV1gYND6Nt9hnt1PN94 github.com/quic-go/quic-go v0.50.1/go.mod h1:Vim6OmUvlYdwBhXP9ZVrtGmCMWa3wEqhq3NgYrI8b4E= github.com/quic-go/webtransport-go v0.8.1-0.20241018022711-4ac2c9250e66 h1:4WFk6u3sOT6pLa1kQ50ZVdm8BQFgJNA117cepZxtLIg= github.com/quic-go/webtransport-go v0.8.1-0.20241018022711-4ac2c9250e66/go.mod h1:Vp72IJajgeOL6ddqrAhmp7IM9zbTcgkQxD/YdxrVwMw= -github.com/raulk/go-watchdog v1.3.0 h1:oUmdlHxdkXRJlwfG0O9omj8ukerm8MEQavSiDTEtBsk= -github.com/raulk/go-watchdog v1.3.0/go.mod h1:fIvOnLbF0b0ZwkB9YU4mOW9Did//4vPZtDqv66NfsMU= github.com/rogpeppe/go-internal v1.13.1 h1:KvO1DLK/DRN07sQ1LQKScxyZJuNnedQ5/wKSR38lUII= github.com/rogpeppe/go-internal v1.13.1/go.mod h1:uMEvuHeurkdAXX61udpOXGD/AzZDWNMNyH2VO9fmH0o= github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g= @@ -389,7 +355,6 @@ github.com/shurcooL/sanitized_anchor_name v0.0.0-20170918181015-86672fcb3f95/go. github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= github.com/shurcooL/users v0.0.0-20180125191416-49c67e49c537/go.mod h1:QJTqeLYEDaXHZDBsXlPCDqdhQuJkuw4NOtaxYe3xii4= github.com/shurcooL/webdavfs v0.0.0-20170829043945-18c3829fa133/go.mod h1:hKmq5kWdCj2z2KEozexVbfEZIWiTjhE0+UjmZgPqehw= -github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= github.com/smartystreets/assertions v1.2.0 h1:42S6lae5dvLc7BrLu/0ugRtcFVjoJNMC/N3yZFZkDFs= github.com/smartystreets/assertions v1.2.0/go.mod h1:tcbTF8ujkAEcZ8TElKY+i30BzYlVhC/LOxJk7iOWnoo= github.com/smartystreets/goconvey v1.7.2 h1:9RBaZCeXEQ3UselpuwUQHltGVXvdwm6cv1hgR6gDIPg= @@ -413,7 +378,6 @@ github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXl github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/tarm/serial v0.0.0-20180830185346-98f6abe2eb07/go.mod h1:kDXzergiv9cbyO7IOYJZWg1U88JhDg3PB6klq9Hg2pA= -github.com/urfave/cli v1.22.2/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= github.com/urfave/cli v1.22.10/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= github.com/viant/assertly v0.4.8/go.mod h1:aGifi++jvCrUaklKEKT0BU95igDNaqkvz+49uaYMPRU= github.com/viant/toolbox v0.24.0/go.mod h1:OxMCG57V0PXuIP2HNQrtJf2CjqdmbrOx5EkMILuUhzM= @@ -439,12 +403,12 @@ go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0= go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo= go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA= go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A= -go.opentelemetry.io/otel v1.33.0 h1:/FerN9bax5LoK51X/sI0SVYrjSE0/yUL7DpxW4K3FWw= -go.opentelemetry.io/otel v1.33.0/go.mod h1:SUUkR6csvUQl+yjReHu5uM3EtVV7MBm5FHKRlNx4I8I= -go.opentelemetry.io/otel/metric v1.33.0 h1:r+JOocAyeRVXD8lZpjdQjzMadVZp2M4WmQ+5WtEnklQ= -go.opentelemetry.io/otel/metric v1.33.0/go.mod h1:L9+Fyctbp6HFTddIxClbQkjtubW6O9QS3Ann/M82u6M= -go.opentelemetry.io/otel/trace v1.33.0 h1:cCJuF7LRjUFso9LPnEAHJDB2pqzp+hbO8eu1qqW2d/s= -go.opentelemetry.io/otel/trace v1.33.0/go.mod h1:uIcdVUZMpTAmz0tI1z04GoVSezK37CbGV4fr1f2nBck= +go.opentelemetry.io/otel v1.34.0 h1:zRLXxLCgL1WyKsPVrgbSdMN4c0FMkDAskSTQP+0hdUY= +go.opentelemetry.io/otel v1.34.0/go.mod h1:OWFPOQ+h4G8xpyjgqo4SxJYdDQ/qmRH+wivy7zzx9oI= +go.opentelemetry.io/otel/metric v1.34.0 h1:+eTR3U0MyfWjRDhmFMxe2SsW64QrZ84AOhvqS7Y+PoQ= +go.opentelemetry.io/otel/metric v1.34.0/go.mod h1:CEDrp0fy2D0MvkXE+dPV7cMi8tWZwX3dmaIhwPOaqHE= +go.opentelemetry.io/otel/trace v1.34.0 h1:+ouXS2V8Rd4hp4580a8q23bg0azF2nI8cqLYnC8mh/k= +go.opentelemetry.io/otel/trace v1.34.0/go.mod h1:Svm7lSjQD7kG7KJ/MUHPVXSDGz2OX4h0M2jHBhmSfRE= go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= go.uber.org/dig v1.18.0 h1:imUL1UiY0Mg4bqbFfsRQO5G4CGRBec/ZujWTvSVp3pw= go.uber.org/dig v1.18.0/go.mod h1:Us0rSJiThwCv2GteUN0Q7OKvU7n5J4dxZ9JKUXozFdE= @@ -539,15 +503,12 @@ golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.11.0 h1:GGz8+XQP4FvTTrjZPzNKTMFtSXH80RAzG+5ghFPgK9w= golang.org/x/sync v0.11.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= -golang.org/x/sys v0.0.0-20180810173357-98c5dad5d1a0/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181029174526-d69651ed3497/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190316082340-a2f829d7f35f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200124204421-9fbb57f87de9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200602225109-6fdc65e7d980/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -588,12 +549,11 @@ golang.org/x/text v0.22.0 h1:bofq7m3/HAFvbF51jz3Q9wLg3jkvSPuiZu/pD1XwgtM= golang.org/x/text v0.22.0/go.mod h1:YRoo4H8PVmsu+E3Ou7cqLVH8oXWIHVoX0jqUWALQhfY= golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.8.0 h1:9i3RxcPv3PZnitoVGMPDKZSq1xW1gK1Xy3ArNOGZfEg= -golang.org/x/time v0.8.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= +golang.org/x/time v0.11.0 h1:/bpjEDfN9tkoN/ryeYHnv5hcMlc8ncjMcM4XBk5NWV0= +golang.org/x/time v0.11.0/go.mod h1:CDIdPxbZBQxdj6cxyCIdrNogrJKMJ7pr37NYpMcMDSg= golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20181030000716-a0a13e073c7b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= diff --git a/examples/webtransport-wasm/README.md b/examples/webtransport-wasm/README.md new file mode 100644 index 0000000000..c17037efdc --- /dev/null +++ b/examples/webtransport-wasm/README.md @@ -0,0 +1,3 @@ +# WebTransport with WebAssembly + +A simple demo of connecting to a libp2p node using WebTransport, compiled to WebAssembly. \ No newline at end of file diff --git a/examples/webtransport-wasm/index.html b/examples/webtransport-wasm/index.html new file mode 100644 index 0000000000..561bb4be6f --- /dev/null +++ b/examples/webtransport-wasm/index.html @@ -0,0 +1,11 @@ + + + + + + Document + + + + + \ No newline at end of file diff --git a/examples/webtransport-wasm/main.go b/examples/webtransport-wasm/main.go new file mode 100644 index 0000000000..e434ed3cd1 --- /dev/null +++ b/examples/webtransport-wasm/main.go @@ -0,0 +1,53 @@ +//go:build js +// +build js + +package main + +import ( + "context" + "fmt" + "log" + + "github.com/libp2p/go-libp2p" + "github.com/libp2p/go-libp2p/core/peer" + libp2pwebtransport "github.com/libp2p/go-libp2p/p2p/transport/webtransport" + "github.com/multiformats/go-multiaddr" +) + +func main() { + ctx := context.Background() + h, err := libp2p.New( + libp2p.Transport(libp2pwebtransport.New), + ) + if err != nil { + panic(err) + } + + // Example multiaddr + multiaddrStr := "/ip4/12.144.75.172/udp/4001/quic-v1/webtransport/certhash/uEiBYjbsQlBmvE2iO7JU6OilEHaokJPgDh9POSXU-T42tVw/certhash/uEiDLMJWuxuf4-2RBP1ln_Ic1uXPnXOpJKFFlFmDJWOH4sg/p2p/12D3KooWFxAMbz588VcN4Ae69nMiGvVscWEyEoA6A3fcJxhSzBFM" + + ma, err := multiaddr.NewMultiaddr(multiaddrStr) + if err != nil { + log.Fatalf("Failed to parse multiaddress: %v", err) + } + + info, err := peer.AddrInfoFromP2pAddr(ma) + if err != nil { + log.Fatalf("Failed to extract peer info: %v", err) + } + + if err := h.Connect(ctx, *info); err != nil { + log.Fatalf("Failed to connect to peer: %v", err) + } + + fmt.Println("Connected to peer:", info.ID) + + remotePeer := info.ID + protocols, err := h.Peerstore().GetProtocols(remotePeer) + + if err != nil { + panic(err) + } + fmt.Println("Protocols of remote peer:", protocols) + select {} +} diff --git a/examples/webtransport-wasm/main.js b/examples/webtransport-wasm/main.js new file mode 100644 index 0000000000..cbe9f423a9 --- /dev/null +++ b/examples/webtransport-wasm/main.js @@ -0,0 +1,6 @@ +import './wasm_exec.js'; + +const go = new Go(); +WebAssembly.instantiateStreaming(fetch("main.wasm"), go.importObject).then((result) => { + go.run(result.instance); +}); \ No newline at end of file diff --git a/examples/webtransport-wasm/wasm_exec.js b/examples/webtransport-wasm/wasm_exec.js new file mode 100644 index 0000000000..d71af9e97e --- /dev/null +++ b/examples/webtransport-wasm/wasm_exec.js @@ -0,0 +1,575 @@ +// Copyright 2018 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +"use strict"; + +(() => { + const enosys = () => { + const err = new Error("not implemented"); + err.code = "ENOSYS"; + return err; + }; + + if (!globalThis.fs) { + let outputBuf = ""; + globalThis.fs = { + constants: { O_WRONLY: -1, O_RDWR: -1, O_CREAT: -1, O_TRUNC: -1, O_APPEND: -1, O_EXCL: -1, O_DIRECTORY: -1 }, // unused + writeSync(fd, buf) { + outputBuf += decoder.decode(buf); + const nl = outputBuf.lastIndexOf("\n"); + if (nl != -1) { + console.log(outputBuf.substring(0, nl)); + outputBuf = outputBuf.substring(nl + 1); + } + return buf.length; + }, + write(fd, buf, offset, length, position, callback) { + if (offset !== 0 || length !== buf.length || position !== null) { + callback(enosys()); + return; + } + const n = this.writeSync(fd, buf); + callback(null, n); + }, + chmod(path, mode, callback) { callback(enosys()); }, + chown(path, uid, gid, callback) { callback(enosys()); }, + close(fd, callback) { callback(enosys()); }, + fchmod(fd, mode, callback) { callback(enosys()); }, + fchown(fd, uid, gid, callback) { callback(enosys()); }, + fstat(fd, callback) { callback(enosys()); }, + fsync(fd, callback) { callback(null); }, + ftruncate(fd, length, callback) { callback(enosys()); }, + lchown(path, uid, gid, callback) { callback(enosys()); }, + link(path, link, callback) { callback(enosys()); }, + lstat(path, callback) { callback(enosys()); }, + mkdir(path, perm, callback) { callback(enosys()); }, + open(path, flags, mode, callback) { callback(enosys()); }, + read(fd, buffer, offset, length, position, callback) { callback(enosys()); }, + readdir(path, callback) { callback(enosys()); }, + readlink(path, callback) { callback(enosys()); }, + rename(from, to, callback) { callback(enosys()); }, + rmdir(path, callback) { callback(enosys()); }, + stat(path, callback) { callback(enosys()); }, + symlink(path, link, callback) { callback(enosys()); }, + truncate(path, length, callback) { callback(enosys()); }, + unlink(path, callback) { callback(enosys()); }, + utimes(path, atime, mtime, callback) { callback(enosys()); }, + }; + } + + if (!globalThis.process) { + globalThis.process = { + getuid() { return -1; }, + getgid() { return -1; }, + geteuid() { return -1; }, + getegid() { return -1; }, + getgroups() { throw enosys(); }, + pid: -1, + ppid: -1, + umask() { throw enosys(); }, + cwd() { throw enosys(); }, + chdir() { throw enosys(); }, + } + } + + if (!globalThis.path) { + globalThis.path = { + resolve(...pathSegments) { + return pathSegments.join("/"); + } + } + } + + if (!globalThis.crypto) { + throw new Error("globalThis.crypto is not available, polyfill required (crypto.getRandomValues only)"); + } + + if (!globalThis.performance) { + throw new Error("globalThis.performance is not available, polyfill required (performance.now only)"); + } + + if (!globalThis.TextEncoder) { + throw new Error("globalThis.TextEncoder is not available, polyfill required"); + } + + if (!globalThis.TextDecoder) { + throw new Error("globalThis.TextDecoder is not available, polyfill required"); + } + + const encoder = new TextEncoder("utf-8"); + const decoder = new TextDecoder("utf-8"); + + globalThis.Go = class { + constructor() { + this.argv = ["js"]; + this.env = {}; + this.exit = (code) => { + if (code !== 0) { + console.warn("exit code:", code); + } + }; + this._exitPromise = new Promise((resolve) => { + this._resolveExitPromise = resolve; + }); + this._pendingEvent = null; + this._scheduledTimeouts = new Map(); + this._nextCallbackTimeoutID = 1; + + const setInt64 = (addr, v) => { + this.mem.setUint32(addr + 0, v, true); + this.mem.setUint32(addr + 4, Math.floor(v / 4294967296), true); + } + + const setInt32 = (addr, v) => { + this.mem.setUint32(addr + 0, v, true); + } + + const getInt64 = (addr) => { + const low = this.mem.getUint32(addr + 0, true); + const high = this.mem.getInt32(addr + 4, true); + return low + high * 4294967296; + } + + const loadValue = (addr) => { + const f = this.mem.getFloat64(addr, true); + if (f === 0) { + return undefined; + } + if (!isNaN(f)) { + return f; + } + + const id = this.mem.getUint32(addr, true); + return this._values[id]; + } + + const storeValue = (addr, v) => { + const nanHead = 0x7FF80000; + + if (typeof v === "number" && v !== 0) { + if (isNaN(v)) { + this.mem.setUint32(addr + 4, nanHead, true); + this.mem.setUint32(addr, 0, true); + return; + } + this.mem.setFloat64(addr, v, true); + return; + } + + if (v === undefined) { + this.mem.setFloat64(addr, 0, true); + return; + } + + let id = this._ids.get(v); + if (id === undefined) { + id = this._idPool.pop(); + if (id === undefined) { + id = this._values.length; + } + this._values[id] = v; + this._goRefCounts[id] = 0; + this._ids.set(v, id); + } + this._goRefCounts[id]++; + let typeFlag = 0; + switch (typeof v) { + case "object": + if (v !== null) { + typeFlag = 1; + } + break; + case "string": + typeFlag = 2; + break; + case "symbol": + typeFlag = 3; + break; + case "function": + typeFlag = 4; + break; + } + this.mem.setUint32(addr + 4, nanHead | typeFlag, true); + this.mem.setUint32(addr, id, true); + } + + const loadSlice = (addr) => { + const array = getInt64(addr + 0); + const len = getInt64(addr + 8); + return new Uint8Array(this._inst.exports.mem.buffer, array, len); + } + + const loadSliceOfValues = (addr) => { + const array = getInt64(addr + 0); + const len = getInt64(addr + 8); + const a = new Array(len); + for (let i = 0; i < len; i++) { + a[i] = loadValue(array + i * 8); + } + return a; + } + + const loadString = (addr) => { + const saddr = getInt64(addr + 0); + const len = getInt64(addr + 8); + return decoder.decode(new DataView(this._inst.exports.mem.buffer, saddr, len)); + } + + const testCallExport = (a, b) => { + this._inst.exports.testExport0(); + return this._inst.exports.testExport(a, b); + } + + const timeOrigin = Date.now() - performance.now(); + this.importObject = { + _gotest: { + add: (a, b) => a + b, + callExport: testCallExport, + }, + gojs: { + // Go's SP does not change as long as no Go code is running. Some operations (e.g. calls, getters and setters) + // may synchronously trigger a Go event handler. This makes Go code get executed in the middle of the imported + // function. A goroutine can switch to a new stack if the current stack is too small (see morestack function). + // This changes the SP, thus we have to update the SP used by the imported function. + + // func wasmExit(code int32) + "runtime.wasmExit": (sp) => { + sp >>>= 0; + const code = this.mem.getInt32(sp + 8, true); + this.exited = true; + delete this._inst; + delete this._values; + delete this._goRefCounts; + delete this._ids; + delete this._idPool; + this.exit(code); + }, + + // func wasmWrite(fd uintptr, p unsafe.Pointer, n int32) + "runtime.wasmWrite": (sp) => { + sp >>>= 0; + const fd = getInt64(sp + 8); + const p = getInt64(sp + 16); + const n = this.mem.getInt32(sp + 24, true); + fs.writeSync(fd, new Uint8Array(this._inst.exports.mem.buffer, p, n)); + }, + + // func resetMemoryDataView() + "runtime.resetMemoryDataView": (sp) => { + sp >>>= 0; + this.mem = new DataView(this._inst.exports.mem.buffer); + }, + + // func nanotime1() int64 + "runtime.nanotime1": (sp) => { + sp >>>= 0; + setInt64(sp + 8, (timeOrigin + performance.now()) * 1000000); + }, + + // func walltime() (sec int64, nsec int32) + "runtime.walltime": (sp) => { + sp >>>= 0; + const msec = (new Date).getTime(); + setInt64(sp + 8, msec / 1000); + this.mem.setInt32(sp + 16, (msec % 1000) * 1000000, true); + }, + + // func scheduleTimeoutEvent(delay int64) int32 + "runtime.scheduleTimeoutEvent": (sp) => { + sp >>>= 0; + const id = this._nextCallbackTimeoutID; + this._nextCallbackTimeoutID++; + this._scheduledTimeouts.set(id, setTimeout( + () => { + this._resume(); + while (this._scheduledTimeouts.has(id)) { + // for some reason Go failed to register the timeout event, log and try again + // (temporary workaround for https://github.com/golang/go/issues/28975) + console.warn("scheduleTimeoutEvent: missed timeout event"); + this._resume(); + } + }, + getInt64(sp + 8), + )); + this.mem.setInt32(sp + 16, id, true); + }, + + // func clearTimeoutEvent(id int32) + "runtime.clearTimeoutEvent": (sp) => { + sp >>>= 0; + const id = this.mem.getInt32(sp + 8, true); + clearTimeout(this._scheduledTimeouts.get(id)); + this._scheduledTimeouts.delete(id); + }, + + // func getRandomData(r []byte) + "runtime.getRandomData": (sp) => { + sp >>>= 0; + crypto.getRandomValues(loadSlice(sp + 8)); + }, + + // func finalizeRef(v ref) + "syscall/js.finalizeRef": (sp) => { + sp >>>= 0; + const id = this.mem.getUint32(sp + 8, true); + this._goRefCounts[id]--; + if (this._goRefCounts[id] === 0) { + const v = this._values[id]; + this._values[id] = null; + this._ids.delete(v); + this._idPool.push(id); + } + }, + + // func stringVal(value string) ref + "syscall/js.stringVal": (sp) => { + sp >>>= 0; + storeValue(sp + 24, loadString(sp + 8)); + }, + + // func valueGet(v ref, p string) ref + "syscall/js.valueGet": (sp) => { + sp >>>= 0; + const result = Reflect.get(loadValue(sp + 8), loadString(sp + 16)); + sp = this._inst.exports.getsp() >>> 0; // see comment above + storeValue(sp + 32, result); + }, + + // func valueSet(v ref, p string, x ref) + "syscall/js.valueSet": (sp) => { + sp >>>= 0; + Reflect.set(loadValue(sp + 8), loadString(sp + 16), loadValue(sp + 32)); + }, + + // func valueDelete(v ref, p string) + "syscall/js.valueDelete": (sp) => { + sp >>>= 0; + Reflect.deleteProperty(loadValue(sp + 8), loadString(sp + 16)); + }, + + // func valueIndex(v ref, i int) ref + "syscall/js.valueIndex": (sp) => { + sp >>>= 0; + storeValue(sp + 24, Reflect.get(loadValue(sp + 8), getInt64(sp + 16))); + }, + + // valueSetIndex(v ref, i int, x ref) + "syscall/js.valueSetIndex": (sp) => { + sp >>>= 0; + Reflect.set(loadValue(sp + 8), getInt64(sp + 16), loadValue(sp + 24)); + }, + + // func valueCall(v ref, m string, args []ref) (ref, bool) + "syscall/js.valueCall": (sp) => { + sp >>>= 0; + try { + const v = loadValue(sp + 8); + const m = Reflect.get(v, loadString(sp + 16)); + const args = loadSliceOfValues(sp + 32); + const result = Reflect.apply(m, v, args); + sp = this._inst.exports.getsp() >>> 0; // see comment above + storeValue(sp + 56, result); + this.mem.setUint8(sp + 64, 1); + } catch (err) { + sp = this._inst.exports.getsp() >>> 0; // see comment above + storeValue(sp + 56, err); + this.mem.setUint8(sp + 64, 0); + } + }, + + // func valueInvoke(v ref, args []ref) (ref, bool) + "syscall/js.valueInvoke": (sp) => { + sp >>>= 0; + try { + const v = loadValue(sp + 8); + const args = loadSliceOfValues(sp + 16); + const result = Reflect.apply(v, undefined, args); + sp = this._inst.exports.getsp() >>> 0; // see comment above + storeValue(sp + 40, result); + this.mem.setUint8(sp + 48, 1); + } catch (err) { + sp = this._inst.exports.getsp() >>> 0; // see comment above + storeValue(sp + 40, err); + this.mem.setUint8(sp + 48, 0); + } + }, + + // func valueNew(v ref, args []ref) (ref, bool) + "syscall/js.valueNew": (sp) => { + sp >>>= 0; + try { + const v = loadValue(sp + 8); + const args = loadSliceOfValues(sp + 16); + const result = Reflect.construct(v, args); + sp = this._inst.exports.getsp() >>> 0; // see comment above + storeValue(sp + 40, result); + this.mem.setUint8(sp + 48, 1); + } catch (err) { + sp = this._inst.exports.getsp() >>> 0; // see comment above + storeValue(sp + 40, err); + this.mem.setUint8(sp + 48, 0); + } + }, + + // func valueLength(v ref) int + "syscall/js.valueLength": (sp) => { + sp >>>= 0; + setInt64(sp + 16, parseInt(loadValue(sp + 8).length)); + }, + + // valuePrepareString(v ref) (ref, int) + "syscall/js.valuePrepareString": (sp) => { + sp >>>= 0; + const str = encoder.encode(String(loadValue(sp + 8))); + storeValue(sp + 16, str); + setInt64(sp + 24, str.length); + }, + + // valueLoadString(v ref, b []byte) + "syscall/js.valueLoadString": (sp) => { + sp >>>= 0; + const str = loadValue(sp + 8); + loadSlice(sp + 16).set(str); + }, + + // func valueInstanceOf(v ref, t ref) bool + "syscall/js.valueInstanceOf": (sp) => { + sp >>>= 0; + this.mem.setUint8(sp + 24, (loadValue(sp + 8) instanceof loadValue(sp + 16)) ? 1 : 0); + }, + + // func copyBytesToGo(dst []byte, src ref) (int, bool) + "syscall/js.copyBytesToGo": (sp) => { + sp >>>= 0; + const dst = loadSlice(sp + 8); + const src = loadValue(sp + 32); + if (!(src instanceof Uint8Array || src instanceof Uint8ClampedArray)) { + this.mem.setUint8(sp + 48, 0); + return; + } + const toCopy = src.subarray(0, dst.length); + dst.set(toCopy); + setInt64(sp + 40, toCopy.length); + this.mem.setUint8(sp + 48, 1); + }, + + // func copyBytesToJS(dst ref, src []byte) (int, bool) + "syscall/js.copyBytesToJS": (sp) => { + sp >>>= 0; + const dst = loadValue(sp + 8); + const src = loadSlice(sp + 16); + if (!(dst instanceof Uint8Array || dst instanceof Uint8ClampedArray)) { + this.mem.setUint8(sp + 48, 0); + return; + } + const toCopy = src.subarray(0, dst.length); + dst.set(toCopy); + setInt64(sp + 40, toCopy.length); + this.mem.setUint8(sp + 48, 1); + }, + + "debug": (value) => { + console.log(value); + }, + } + }; + } + + async run(instance) { + if (!(instance instanceof WebAssembly.Instance)) { + throw new Error("Go.run: WebAssembly.Instance expected"); + } + this._inst = instance; + this.mem = new DataView(this._inst.exports.mem.buffer); + this._values = [ // JS values that Go currently has references to, indexed by reference id + NaN, + 0, + null, + true, + false, + globalThis, + this, + ]; + this._goRefCounts = new Array(this._values.length).fill(Infinity); // number of references that Go has to a JS value, indexed by reference id + this._ids = new Map([ // mapping from JS values to reference ids + [0, 1], + [null, 2], + [true, 3], + [false, 4], + [globalThis, 5], + [this, 6], + ]); + this._idPool = []; // unused ids that have been garbage collected + this.exited = false; // whether the Go program has exited + + // Pass command line arguments and environment variables to WebAssembly by writing them to the linear memory. + let offset = 4096; + + const strPtr = (str) => { + const ptr = offset; + const bytes = encoder.encode(str + "\0"); + new Uint8Array(this.mem.buffer, offset, bytes.length).set(bytes); + offset += bytes.length; + if (offset % 8 !== 0) { + offset += 8 - (offset % 8); + } + return ptr; + }; + + const argc = this.argv.length; + + const argvPtrs = []; + this.argv.forEach((arg) => { + argvPtrs.push(strPtr(arg)); + }); + argvPtrs.push(0); + + const keys = Object.keys(this.env).sort(); + keys.forEach((key) => { + argvPtrs.push(strPtr(`${key}=${this.env[key]}`)); + }); + argvPtrs.push(0); + + const argv = offset; + argvPtrs.forEach((ptr) => { + this.mem.setUint32(offset, ptr, true); + this.mem.setUint32(offset + 4, 0, true); + offset += 8; + }); + + // The linker guarantees global data starts from at least wasmMinDataAddr. + // Keep in sync with cmd/link/internal/ld/data.go:wasmMinDataAddr. + const wasmMinDataAddr = 4096 + 8192; + if (offset >= wasmMinDataAddr) { + throw new Error("total length of command line and environment variables exceeds limit"); + } + + this._inst.exports.run(argc, argv); + if (this.exited) { + this._resolveExitPromise(); + } + await this._exitPromise; + } + + _resume() { + if (this.exited) { + throw new Error("Go program has already exited"); + } + this._inst.exports.resume(); + if (this.exited) { + this._resolveExitPromise(); + } + } + + _makeFuncWrapper(id) { + const go = this; + return function () { + const event = { id: id, this: this, args: arguments }; + go._pendingEvent = event; + go._resume(); + return event.result; + }; + } + } +})(); diff --git a/go.mod b/go.mod index f34023fccb..17fcf49508 100644 --- a/go.mod +++ b/go.mod @@ -104,7 +104,7 @@ require ( github.com/spaolacci/murmur3 v1.1.0 // indirect github.com/wlynxg/anet v0.0.5 // indirect go.uber.org/dig v1.18.0 // indirect - go.uber.org/multierr v1.11.0 // indirect + go.uber.org/multierr v1.11.0 golang.org/x/exp v0.0.0-20250218142911-aa4b98e5adaa // indirect golang.org/x/mod v0.23.0 // indirect golang.org/x/net v0.35.0 // indirect diff --git a/libp2p_test.go b/libp2p_test.go index f10bbb9b75..64d12e7d40 100644 --- a/libp2p_test.go +++ b/libp2p_test.go @@ -1,3 +1,6 @@ +//go:build !js +// +build !js + package libp2p import ( diff --git a/options.go b/options.go index 0329b7e60b..4818e37c59 100644 --- a/options.go +++ b/options.go @@ -1,104 +1,16 @@ -package libp2p +//go:build !js +// +build !js -// This file contains all libp2p configuration options (except the defaults, -// those are in defaults.go). +package libp2p import ( - "crypto/rand" - "encoding/binary" "errors" - "fmt" "reflect" - "time" - "github.com/libp2p/go-libp2p/config" - "github.com/libp2p/go-libp2p/core/connmgr" - "github.com/libp2p/go-libp2p/core/crypto" - "github.com/libp2p/go-libp2p/core/metrics" - "github.com/libp2p/go-libp2p/core/network" - "github.com/libp2p/go-libp2p/core/peer" - "github.com/libp2p/go-libp2p/core/peerstore" - "github.com/libp2p/go-libp2p/core/pnet" - "github.com/libp2p/go-libp2p/core/protocol" - "github.com/libp2p/go-libp2p/core/transport" - "github.com/libp2p/go-libp2p/p2p/host/autorelay" - bhost "github.com/libp2p/go-libp2p/p2p/host/basic" - "github.com/libp2p/go-libp2p/p2p/net/swarm" - tptu "github.com/libp2p/go-libp2p/p2p/net/upgrader" - relayv2 "github.com/libp2p/go-libp2p/p2p/protocol/circuitv2/relay" - "github.com/libp2p/go-libp2p/p2p/protocol/holepunch" "github.com/libp2p/go-libp2p/p2p/transport/quicreuse" - "github.com/prometheus/client_golang/prometheus" - - ma "github.com/multiformats/go-multiaddr" "go.uber.org/fx" ) -// ListenAddrStrings configures libp2p to listen on the given (unparsed) -// addresses. -func ListenAddrStrings(s ...string) Option { - return func(cfg *Config) error { - for _, addrstr := range s { - a, err := ma.NewMultiaddr(addrstr) - if err != nil { - return err - } - cfg.ListenAddrs = append(cfg.ListenAddrs, a) - } - return nil - } -} - -// ListenAddrs configures libp2p to listen on the given addresses. -func ListenAddrs(addrs ...ma.Multiaddr) Option { - return func(cfg *Config) error { - cfg.ListenAddrs = append(cfg.ListenAddrs, addrs...) - return nil - } -} - -// Security configures libp2p to use the given security transport (or transport -// constructor). -// -// Name is the protocol name. -// -// The transport can be a constructed security.Transport or a function taking -// any subset of this libp2p node's: -// * Public key -// * Private key -// * Peer ID -// * Host -// * Network -// * Peerstore -func Security(name string, constructor interface{}) Option { - return func(cfg *Config) error { - if cfg.Insecure { - return fmt.Errorf("cannot use security transports with an insecure libp2p configuration") - } - cfg.SecurityTransports = append(cfg.SecurityTransports, config.Security{ID: protocol.ID(name), Constructor: constructor}) - return nil - } -} - -// NoSecurity is an option that completely disables all transport security. -// It's incompatible with all other transport security protocols. -var NoSecurity Option = func(cfg *Config) error { - if len(cfg.SecurityTransports) > 0 { - return fmt.Errorf("cannot use security transports with an insecure libp2p configuration") - } - cfg.Insecure = true - return nil -} - -// Muxer configures libp2p to use the given stream multiplexer. -// name is the protocol name. -func Muxer(name string, muxer network.Multiplexer) Option { - return func(cfg *Config) error { - cfg.Muxers = append(cfg.Muxers, tptu.StreamMuxer{Muxer: muxer, ID: protocol.ID(name)}) - return nil - } -} - func QUICReuse(constructor interface{}, opts ...quicreuse.Option) Option { return func(cfg *Config) error { tag := `group:"quicreuseopts"` @@ -125,533 +37,3 @@ func QUICReuse(constructor interface{}, opts ...quicreuse.Option) Option { return nil } } - -// Transport configures libp2p to use the given transport (or transport -// constructor). -// -// The transport can be a constructed transport.Transport or a function taking -// any subset of this libp2p node's: -// * Transport Upgrader (*tptu.Upgrader) -// * Host -// * Stream muxer (muxer.Transport) -// * Security transport (security.Transport) -// * Private network protector (pnet.Protector) -// * Peer ID -// * Private Key -// * Public Key -// * Address filter (filter.Filter) -// * Peerstore -func Transport(constructor interface{}, opts ...interface{}) Option { - return func(cfg *Config) error { - // generate a random identifier, so that fx can associate the constructor with its options - b := make([]byte, 8) - rand.Read(b) - id := binary.BigEndian.Uint64(b) - - tag := fmt.Sprintf(`group:"transportopt_%d"`, id) - - typ := reflect.ValueOf(constructor).Type() - numParams := typ.NumIn() - isVariadic := typ.IsVariadic() - - if !isVariadic && len(opts) > 0 { - return errors.New("transport constructor doesn't take any options") - } - if isVariadic && numParams >= 1 { - paramType := typ.In(numParams - 1).Elem() - for _, opt := range opts { - if typ := reflect.TypeOf(opt); !typ.AssignableTo(paramType) { - return fmt.Errorf("transport option of type %s not assignable to %s", typ, paramType) - } - } - } - - var params []string - if isVariadic && len(opts) > 0 { - // If there are transport options, apply the tag. - // Since options are variadic, they have to be the last argument of the constructor. - params = make([]string, numParams) - params[len(params)-1] = tag - } - - cfg.Transports = append(cfg.Transports, fx.Provide( - fx.Annotate( - constructor, - fx.ParamTags(params...), - fx.As(new(transport.Transport)), - fx.ResultTags(`group:"transport"`), - ), - )) - for _, opt := range opts { - cfg.Transports = append(cfg.Transports, fx.Supply( - fx.Annotate( - opt, - fx.ResultTags(tag), - ), - )) - } - return nil - } -} - -// Peerstore configures libp2p to use the given peerstore. -func Peerstore(ps peerstore.Peerstore) Option { - return func(cfg *Config) error { - if cfg.Peerstore != nil { - return fmt.Errorf("cannot specify multiple peerstore options") - } - - cfg.Peerstore = ps - return nil - } -} - -// PrivateNetwork configures libp2p to use the given private network protector. -func PrivateNetwork(psk pnet.PSK) Option { - return func(cfg *Config) error { - if cfg.PSK != nil { - return fmt.Errorf("cannot specify multiple private network options") - } - - cfg.PSK = psk - return nil - } -} - -// BandwidthReporter configures libp2p to use the given bandwidth reporter. -func BandwidthReporter(rep metrics.Reporter) Option { - return func(cfg *Config) error { - if cfg.Reporter != nil { - return fmt.Errorf("cannot specify multiple bandwidth reporter options") - } - - cfg.Reporter = rep - return nil - } -} - -// Identity configures libp2p to use the given private key to identify itself. -func Identity(sk crypto.PrivKey) Option { - return func(cfg *Config) error { - if cfg.PeerKey != nil { - return fmt.Errorf("cannot specify multiple identities") - } - - cfg.PeerKey = sk - return nil - } -} - -// ConnectionManager configures libp2p to use the given connection manager. -// -// The current "standard" connection manager lives in github.com/libp2p/go-libp2p-connmgr. See -// https://pkg.go.dev/github.com/libp2p/go-libp2p-connmgr?utm_source=godoc#NewConnManager. -func ConnectionManager(connman connmgr.ConnManager) Option { - return func(cfg *Config) error { - if cfg.ConnManager != nil { - return fmt.Errorf("cannot specify multiple connection managers") - } - cfg.ConnManager = connman - return nil - } -} - -// AddrsFactory configures libp2p to use the given address factory. -func AddrsFactory(factory config.AddrsFactory) Option { - return func(cfg *Config) error { - if cfg.AddrsFactory != nil { - return fmt.Errorf("cannot specify multiple address factories") - } - cfg.AddrsFactory = factory - return nil - } -} - -// EnableRelay configures libp2p to enable the relay transport. -// This option only configures libp2p to accept inbound connections from relays -// and make outbound connections_through_ relays when requested by the remote peer. -// This option supports both circuit v1 and v2 connections. -// (default: enabled) -func EnableRelay() Option { - return func(cfg *Config) error { - cfg.RelayCustom = true - cfg.Relay = true - return nil - } -} - -// DisableRelay configures libp2p to disable the relay transport. -func DisableRelay() Option { - return func(cfg *Config) error { - cfg.RelayCustom = true - cfg.Relay = false - return nil - } -} - -// EnableRelayService configures libp2p to run a circuit v2 relay, -// if we detect that we're publicly reachable. -func EnableRelayService(opts ...relayv2.Option) Option { - return func(cfg *Config) error { - cfg.EnableRelayService = true - cfg.RelayServiceOpts = opts - return nil - } -} - -// EnableAutoRelay configures libp2p to enable the AutoRelay subsystem. -// -// Dependencies: -// - Relay (enabled by default) -// - Either: -// 1. A list of static relays -// 2. A PeerSource function that provides a chan of relays. See `autorelay.WithPeerSource` -// -// This subsystem performs automatic address rewriting to advertise relay addresses when it -// detects that the node is publicly unreachable (e.g. behind a NAT). -// -// Deprecated: Use EnableAutoRelayWithStaticRelays or EnableAutoRelayWithPeerSource -func EnableAutoRelay(opts ...autorelay.Option) Option { - return func(cfg *Config) error { - cfg.EnableAutoRelay = true - cfg.AutoRelayOpts = opts - return nil - } -} - -// EnableAutoRelayWithStaticRelays configures libp2p to enable the AutoRelay subsystem using -// the provided relays as relay candidates. -// This subsystem performs automatic address rewriting to advertise relay addresses when it -// detects that the node is publicly unreachable (e.g. behind a NAT). -func EnableAutoRelayWithStaticRelays(static []peer.AddrInfo, opts ...autorelay.Option) Option { - return func(cfg *Config) error { - cfg.EnableAutoRelay = true - cfg.AutoRelayOpts = append([]autorelay.Option{autorelay.WithStaticRelays(static)}, opts...) - return nil - } -} - -// EnableAutoRelayWithPeerSource configures libp2p to enable the AutoRelay -// subsystem using the provided PeerSource callback to get more relay -// candidates. This subsystem performs automatic address rewriting to advertise -// relay addresses when it detects that the node is publicly unreachable (e.g. -// behind a NAT). -func EnableAutoRelayWithPeerSource(peerSource autorelay.PeerSource, opts ...autorelay.Option) Option { - return func(cfg *Config) error { - cfg.EnableAutoRelay = true - cfg.AutoRelayOpts = append([]autorelay.Option{autorelay.WithPeerSource(peerSource)}, opts...) - return nil - } -} - -// ForceReachabilityPublic overrides automatic reachability detection in the AutoNAT subsystem, -// forcing the local node to believe it is reachable externally. -func ForceReachabilityPublic() Option { - return func(cfg *Config) error { - public := network.ReachabilityPublic - cfg.AutoNATConfig.ForceReachability = &public - return nil - } -} - -// ForceReachabilityPrivate overrides automatic reachability detection in the AutoNAT subsystem, -// forceing the local node to believe it is behind a NAT and not reachable externally. -func ForceReachabilityPrivate() Option { - return func(cfg *Config) error { - private := network.ReachabilityPrivate - cfg.AutoNATConfig.ForceReachability = &private - return nil - } -} - -// EnableNATService configures libp2p to provide a service to peers for determining -// their reachability status. When enabled, the host will attempt to dial back -// to peers, and then tell them if it was successful in making such connections. -func EnableNATService() Option { - return func(cfg *Config) error { - cfg.AutoNATConfig.EnableService = true - return nil - } -} - -// AutoNATServiceRateLimit changes the default rate limiting configured in helping -// other peers determine their reachability status. When set, the host will limit -// the number of requests it responds to in each 60 second period to the set -// numbers. A value of '0' disables throttling. -func AutoNATServiceRateLimit(global, perPeer int, interval time.Duration) Option { - return func(cfg *Config) error { - cfg.AutoNATConfig.ThrottleGlobalLimit = global - cfg.AutoNATConfig.ThrottlePeerLimit = perPeer - cfg.AutoNATConfig.ThrottleInterval = interval - return nil - } -} - -// ConnectionGater configures libp2p to use the given ConnectionGater -// to actively reject inbound/outbound connections based on the lifecycle stage -// of the connection. -// -// For more information, refer to go-libp2p/core.ConnectionGater. -func ConnectionGater(cg connmgr.ConnectionGater) Option { - return func(cfg *Config) error { - if cfg.ConnectionGater != nil { - return errors.New("cannot configure multiple connection gaters, or cannot configure both Filters and ConnectionGater") - } - cfg.ConnectionGater = cg - return nil - } -} - -// ResourceManager configures libp2p to use the given ResourceManager. -// When using the p2p/host/resource-manager implementation of the ResourceManager interface, -// it is recommended to set limits for libp2p protocol by calling SetDefaultServiceLimits. -func ResourceManager(rcmgr network.ResourceManager) Option { - return func(cfg *Config) error { - if cfg.ResourceManager != nil { - return errors.New("cannot configure multiple resource managers") - } - cfg.ResourceManager = rcmgr - return nil - } -} - -// NATPortMap configures libp2p to use the default NATManager. The default -// NATManager will attempt to open a port in your network's firewall using UPnP. -func NATPortMap() Option { - return NATManager(bhost.NewNATManager) -} - -// NATManager will configure libp2p to use the requested NATManager. This -// function should be passed a NATManager *constructor* that takes a libp2p Network. -func NATManager(nm config.NATManagerC) Option { - return func(cfg *Config) error { - if cfg.NATManager != nil { - return fmt.Errorf("cannot specify multiple NATManagers") - } - cfg.NATManager = nm - return nil - } -} - -// Ping will configure libp2p to support the ping service; enable by default. -func Ping(enable bool) Option { - return func(cfg *Config) error { - cfg.DisablePing = !enable - return nil - } -} - -// Routing will configure libp2p to use routing. -func Routing(rt config.RoutingC) Option { - return func(cfg *Config) error { - if cfg.Routing != nil { - return fmt.Errorf("cannot specify multiple routing options") - } - cfg.Routing = rt - return nil - } -} - -// NoListenAddrs will configure libp2p to not listen by default. -// -// This will both clear any configured listen addrs and prevent libp2p from -// applying the default listen address option. It also disables relay, unless the -// user explicitly specifies with an option, as the transport creates an implicit -// listen address that would make the node dialable through any relay it was connected to. -var NoListenAddrs = func(cfg *Config) error { - cfg.ListenAddrs = []ma.Multiaddr{} - if !cfg.RelayCustom { - cfg.RelayCustom = true - cfg.Relay = false - } - return nil -} - -// NoTransports will configure libp2p to not enable any transports. -// -// This will both clear any configured transports (specified in prior libp2p -// options) and prevent libp2p from applying the default transports. -var NoTransports = func(cfg *Config) error { - cfg.Transports = []fx.Option{} - return nil -} - -// ProtocolVersion sets the protocolVersion string required by the -// libp2p Identify protocol. -func ProtocolVersion(s string) Option { - return func(cfg *Config) error { - cfg.ProtocolVersion = s - return nil - } -} - -// UserAgent sets the libp2p user-agent sent along with the identify protocol -func UserAgent(userAgent string) Option { - return func(cfg *Config) error { - cfg.UserAgent = userAgent - return nil - } -} - -// MultiaddrResolver sets the libp2p dns resolver -func MultiaddrResolver(rslv network.MultiaddrDNSResolver) Option { - return func(cfg *Config) error { - cfg.MultiaddrResolver = rslv - return nil - } -} - -// Experimental -// EnableHolePunching enables NAT traversal by enabling NATT'd peers to both initiate and respond to hole punching attempts -// to create direct/NAT-traversed connections with other peers. (default: disabled) -// -// Dependencies: -// - Relay (enabled by default) -// -// This subsystem performs two functions: -// -// 1. On receiving an inbound Relay connection, it attempts to create a direct connection with the remote peer -// by initiating and co-ordinating a hole punch over the Relayed connection. -// 2. If a peer sees a request to co-ordinate a hole punch on an outbound Relay connection, -// it will participate in the hole-punch to create a direct connection with the remote peer. -// -// If the hole punch is successful, all new streams will thereafter be created on the hole-punched connection. -// The Relayed connection will eventually be closed after a grace period. -// -// All existing indefinite long-lived streams on the Relayed connection will have to re-opened on the hole-punched connection by the user. -// Users can make use of the `Connected`/`Disconnected` notifications emitted by the Network for this purpose. -// -// It is not mandatory but nice to also enable the `AutoRelay` option (See `EnableAutoRelay`) -// so the peer can discover and connect to Relay servers if it discovers that it is NATT'd and has private reachability via AutoNAT. -// This will then enable it to advertise Relay addresses which can be used to accept inbound Relay connections to then co-ordinate -// a hole punch. -// -// If `EnableAutoRelay` is configured and the user is confident that the peer has private reachability/is NATT'd, -// the `ForceReachabilityPrivate` option can be configured to short-circuit reachability discovery via AutoNAT -// so the peer can immediately start connecting to Relay servers. -// -// If `EnableAutoRelay` is configured, the `StaticRelays` option can be used to configure a static set of Relay servers -// for `AutoRelay` to connect to so that it does not need to discover Relay servers via Routing. -func EnableHolePunching(opts ...holepunch.Option) Option { - return func(cfg *Config) error { - cfg.EnableHolePunching = true - cfg.HolePunchingOptions = opts - return nil - } -} - -func WithDialTimeout(t time.Duration) Option { - return func(cfg *Config) error { - if t <= 0 { - return errors.New("dial timeout needs to be non-negative") - } - cfg.DialTimeout = t - return nil - } -} - -// DisableMetrics configures libp2p to disable prometheus metrics -func DisableMetrics() Option { - return func(cfg *Config) error { - cfg.DisableMetrics = true - return nil - } -} - -// PrometheusRegisterer configures libp2p to use reg as the Registerer for all metrics subsystems -func PrometheusRegisterer(reg prometheus.Registerer) Option { - return func(cfg *Config) error { - if cfg.DisableMetrics { - return errors.New("cannot set registerer when metrics are disabled") - } - if cfg.PrometheusRegisterer != nil { - return errors.New("registerer already set") - } - if reg == nil { - return errors.New("registerer cannot be nil") - } - cfg.PrometheusRegisterer = reg - return nil - } -} - -// DialRanker configures libp2p to use d as the dial ranker. To enable smart -// dialing use `swarm.DefaultDialRanker`. use `swarm.NoDelayDialRanker` to -// disable smart dialing. -// -// Deprecated: use SwarmOpts(swarm.WithDialRanker(d)) instead -func DialRanker(d network.DialRanker) Option { - return func(cfg *Config) error { - if cfg.DialRanker != nil { - return errors.New("dial ranker already configured") - } - cfg.DialRanker = d - return nil - } -} - -// SwarmOpts configures libp2p to use swarm with opts -func SwarmOpts(opts ...swarm.Option) Option { - return func(cfg *Config) error { - cfg.SwarmOpts = opts - return nil - } -} - -// DisableIdentifyAddressDiscovery disables address discovery using peer provided observed addresses -// in identify. If you know your public addresses upfront, the recommended way is to use -// AddressFactory to provide the external adddress to the host and use this option to disable -// discovery from identify. -func DisableIdentifyAddressDiscovery() Option { - return func(cfg *Config) error { - cfg.DisableIdentifyAddressDiscovery = true - return nil - } -} - -// EnableAutoNATv2 enables autonat v2 -func EnableAutoNATv2() Option { - return func(cfg *Config) error { - cfg.EnableAutoNATv2 = true - return nil - } -} - -// UDPBlackHoleSuccessCounter configures libp2p to use f as the black hole filter for UDP addrs -func UDPBlackHoleSuccessCounter(f *swarm.BlackHoleSuccessCounter) Option { - return func(cfg *Config) error { - cfg.UDPBlackHoleSuccessCounter = f - cfg.CustomUDPBlackHoleSuccessCounter = true - return nil - } -} - -// IPv6BlackHoleSuccessCounter configures libp2p to use f as the black hole filter for IPv6 addrs -func IPv6BlackHoleSuccessCounter(f *swarm.BlackHoleSuccessCounter) Option { - return func(cfg *Config) error { - cfg.IPv6BlackHoleSuccessCounter = f - cfg.CustomIPv6BlackHoleSuccessCounter = true - return nil - } -} - -// WithFxOption adds a user provided fx.Option to the libp2p constructor. -// Experimental: This option is subject to change or removal. -func WithFxOption(opts ...fx.Option) Option { - return func(cfg *Config) error { - cfg.UserFxOptions = append(cfg.UserFxOptions, opts...) - return nil - } -} - -// ShareTCPListener shares the same listen address between TCP and Websocket -// transports. This lets both transports use the same TCP port. -// -// Currently this behavior is Opt-in. In a future release this will be the -// default, and this option will be removed. -func ShareTCPListener() Option { - return func(cfg *Config) error { - cfg.ShareTCPListener = true - return nil - } -} diff --git a/options_shared.go b/options_shared.go new file mode 100644 index 0000000000..c982c03438 --- /dev/null +++ b/options_shared.go @@ -0,0 +1,629 @@ +package libp2p + +// This file contains all libp2p configuration options (except the defaults, +// those are in defaults.go). + +import ( + "crypto/rand" + "encoding/binary" + "errors" + "fmt" + "reflect" + "time" + + "github.com/libp2p/go-libp2p/config" + "github.com/libp2p/go-libp2p/core/connmgr" + "github.com/libp2p/go-libp2p/core/crypto" + "github.com/libp2p/go-libp2p/core/metrics" + "github.com/libp2p/go-libp2p/core/network" + "github.com/libp2p/go-libp2p/core/peer" + "github.com/libp2p/go-libp2p/core/peerstore" + "github.com/libp2p/go-libp2p/core/pnet" + "github.com/libp2p/go-libp2p/core/protocol" + "github.com/libp2p/go-libp2p/core/transport" + "github.com/libp2p/go-libp2p/p2p/host/autorelay" + bhost "github.com/libp2p/go-libp2p/p2p/host/basic" + "github.com/libp2p/go-libp2p/p2p/net/swarm" + tptu "github.com/libp2p/go-libp2p/p2p/net/upgrader" + relayv2 "github.com/libp2p/go-libp2p/p2p/protocol/circuitv2/relay" + "github.com/libp2p/go-libp2p/p2p/protocol/holepunch" + "github.com/prometheus/client_golang/prometheus" + + ma "github.com/multiformats/go-multiaddr" + "go.uber.org/fx" +) + +// ListenAddrStrings configures libp2p to listen on the given (unparsed) +// addresses. +func ListenAddrStrings(s ...string) Option { + return func(cfg *Config) error { + for _, addrstr := range s { + a, err := ma.NewMultiaddr(addrstr) + if err != nil { + return err + } + cfg.ListenAddrs = append(cfg.ListenAddrs, a) + } + return nil + } +} + +// ListenAddrs configures libp2p to listen on the given addresses. +func ListenAddrs(addrs ...ma.Multiaddr) Option { + return func(cfg *Config) error { + cfg.ListenAddrs = append(cfg.ListenAddrs, addrs...) + return nil + } +} + +// Security configures libp2p to use the given security transport (or transport +// constructor). +// +// Name is the protocol name. +// +// The transport can be a constructed security.Transport or a function taking +// any subset of this libp2p node's: +// * Public key +// * Private key +// * Peer ID +// * Host +// * Network +// * Peerstore +func Security(name string, constructor interface{}) Option { + return func(cfg *Config) error { + if cfg.Insecure { + return fmt.Errorf("cannot use security transports with an insecure libp2p configuration") + } + cfg.SecurityTransports = append(cfg.SecurityTransports, config.Security{ID: protocol.ID(name), Constructor: constructor}) + return nil + } +} + +// NoSecurity is an option that completely disables all transport security. +// It's incompatible with all other transport security protocols. +var NoSecurity Option = func(cfg *Config) error { + if len(cfg.SecurityTransports) > 0 { + return fmt.Errorf("cannot use security transports with an insecure libp2p configuration") + } + cfg.Insecure = true + return nil +} + +// Muxer configures libp2p to use the given stream multiplexer. +// name is the protocol name. +func Muxer(name string, muxer network.Multiplexer) Option { + return func(cfg *Config) error { + cfg.Muxers = append(cfg.Muxers, tptu.StreamMuxer{Muxer: muxer, ID: protocol.ID(name)}) + return nil + } +} + +// Transport configures libp2p to use the given transport (or transport +// constructor). +// +// The transport can be a constructed transport.Transport or a function taking +// any subset of this libp2p node's: +// * Transport Upgrader (*tptu.Upgrader) +// * Host +// * Stream muxer (muxer.Transport) +// * Security transport (security.Transport) +// * Private network protector (pnet.Protector) +// * Peer ID +// * Private Key +// * Public Key +// * Address filter (filter.Filter) +// * Peerstore +func Transport(constructor interface{}, opts ...interface{}) Option { + return func(cfg *Config) error { + // generate a random identifier, so that fx can associate the constructor with its options + b := make([]byte, 8) + rand.Read(b) + id := binary.BigEndian.Uint64(b) + + tag := fmt.Sprintf(`group:"transportopt_%d"`, id) + + typ := reflect.ValueOf(constructor).Type() + numParams := typ.NumIn() + isVariadic := typ.IsVariadic() + + if !isVariadic && len(opts) > 0 { + return errors.New("transport constructor doesn't take any options") + } + if isVariadic && numParams >= 1 { + paramType := typ.In(numParams - 1).Elem() + for _, opt := range opts { + if typ := reflect.TypeOf(opt); !typ.AssignableTo(paramType) { + return fmt.Errorf("transport option of type %s not assignable to %s", typ, paramType) + } + } + } + + var params []string + if isVariadic && len(opts) > 0 { + // If there are transport options, apply the tag. + // Since options are variadic, they have to be the last argument of the constructor. + params = make([]string, numParams) + params[len(params)-1] = tag + } + + cfg.Transports = append(cfg.Transports, fx.Provide( + fx.Annotate( + constructor, + fx.ParamTags(params...), + fx.As(new(transport.Transport)), + fx.ResultTags(`group:"transport"`), + ), + )) + for _, opt := range opts { + cfg.Transports = append(cfg.Transports, fx.Supply( + fx.Annotate( + opt, + fx.ResultTags(tag), + ), + )) + } + return nil + } +} + +// Peerstore configures libp2p to use the given peerstore. +func Peerstore(ps peerstore.Peerstore) Option { + return func(cfg *Config) error { + if cfg.Peerstore != nil { + return fmt.Errorf("cannot specify multiple peerstore options") + } + + cfg.Peerstore = ps + return nil + } +} + +// PrivateNetwork configures libp2p to use the given private network protector. +func PrivateNetwork(psk pnet.PSK) Option { + return func(cfg *Config) error { + if cfg.PSK != nil { + return fmt.Errorf("cannot specify multiple private network options") + } + + cfg.PSK = psk + return nil + } +} + +// BandwidthReporter configures libp2p to use the given bandwidth reporter. +func BandwidthReporter(rep metrics.Reporter) Option { + return func(cfg *Config) error { + if cfg.Reporter != nil { + return fmt.Errorf("cannot specify multiple bandwidth reporter options") + } + + cfg.Reporter = rep + return nil + } +} + +// Identity configures libp2p to use the given private key to identify itself. +func Identity(sk crypto.PrivKey) Option { + return func(cfg *Config) error { + if cfg.PeerKey != nil { + return fmt.Errorf("cannot specify multiple identities") + } + + cfg.PeerKey = sk + return nil + } +} + +// ConnectionManager configures libp2p to use the given connection manager. +// +// The current "standard" connection manager lives in github.com/libp2p/go-libp2p-connmgr. See +// https://pkg.go.dev/github.com/libp2p/go-libp2p-connmgr?utm_source=godoc#NewConnManager. +func ConnectionManager(connman connmgr.ConnManager) Option { + return func(cfg *Config) error { + if cfg.ConnManager != nil { + return fmt.Errorf("cannot specify multiple connection managers") + } + cfg.ConnManager = connman + return nil + } +} + +// AddrsFactory configures libp2p to use the given address factory. +func AddrsFactory(factory config.AddrsFactory) Option { + return func(cfg *Config) error { + if cfg.AddrsFactory != nil { + return fmt.Errorf("cannot specify multiple address factories") + } + cfg.AddrsFactory = factory + return nil + } +} + +// EnableRelay configures libp2p to enable the relay transport. +// This option only configures libp2p to accept inbound connections from relays +// and make outbound connections_through_ relays when requested by the remote peer. +// This option supports both circuit v1 and v2 connections. +// (default: enabled) +func EnableRelay() Option { + return func(cfg *Config) error { + cfg.RelayCustom = true + cfg.Relay = true + return nil + } +} + +// DisableRelay configures libp2p to disable the relay transport. +func DisableRelay() Option { + return func(cfg *Config) error { + cfg.RelayCustom = true + cfg.Relay = false + return nil + } +} + +// EnableRelayService configures libp2p to run a circuit v2 relay, +// if we detect that we're publicly reachable. +func EnableRelayService(opts ...relayv2.Option) Option { + return func(cfg *Config) error { + cfg.EnableRelayService = true + cfg.RelayServiceOpts = opts + return nil + } +} + +// EnableAutoRelay configures libp2p to enable the AutoRelay subsystem. +// +// Dependencies: +// - Relay (enabled by default) +// - Either: +// 1. A list of static relays +// 2. A PeerSource function that provides a chan of relays. See `autorelay.WithPeerSource` +// +// This subsystem performs automatic address rewriting to advertise relay addresses when it +// detects that the node is publicly unreachable (e.g. behind a NAT). +// +// Deprecated: Use EnableAutoRelayWithStaticRelays or EnableAutoRelayWithPeerSource +func EnableAutoRelay(opts ...autorelay.Option) Option { + return func(cfg *Config) error { + cfg.EnableAutoRelay = true + cfg.AutoRelayOpts = opts + return nil + } +} + +// EnableAutoRelayWithStaticRelays configures libp2p to enable the AutoRelay subsystem using +// the provided relays as relay candidates. +// This subsystem performs automatic address rewriting to advertise relay addresses when it +// detects that the node is publicly unreachable (e.g. behind a NAT). +func EnableAutoRelayWithStaticRelays(static []peer.AddrInfo, opts ...autorelay.Option) Option { + return func(cfg *Config) error { + cfg.EnableAutoRelay = true + cfg.AutoRelayOpts = append([]autorelay.Option{autorelay.WithStaticRelays(static)}, opts...) + return nil + } +} + +// EnableAutoRelayWithPeerSource configures libp2p to enable the AutoRelay +// subsystem using the provided PeerSource callback to get more relay +// candidates. This subsystem performs automatic address rewriting to advertise +// relay addresses when it detects that the node is publicly unreachable (e.g. +// behind a NAT). +func EnableAutoRelayWithPeerSource(peerSource autorelay.PeerSource, opts ...autorelay.Option) Option { + return func(cfg *Config) error { + cfg.EnableAutoRelay = true + cfg.AutoRelayOpts = append([]autorelay.Option{autorelay.WithPeerSource(peerSource)}, opts...) + return nil + } +} + +// ForceReachabilityPublic overrides automatic reachability detection in the AutoNAT subsystem, +// forcing the local node to believe it is reachable externally. +func ForceReachabilityPublic() Option { + return func(cfg *Config) error { + public := network.ReachabilityPublic + cfg.AutoNATConfig.ForceReachability = &public + return nil + } +} + +// ForceReachabilityPrivate overrides automatic reachability detection in the AutoNAT subsystem, +// forceing the local node to believe it is behind a NAT and not reachable externally. +func ForceReachabilityPrivate() Option { + return func(cfg *Config) error { + private := network.ReachabilityPrivate + cfg.AutoNATConfig.ForceReachability = &private + return nil + } +} + +// EnableNATService configures libp2p to provide a service to peers for determining +// their reachability status. When enabled, the host will attempt to dial back +// to peers, and then tell them if it was successful in making such connections. +func EnableNATService() Option { + return func(cfg *Config) error { + cfg.AutoNATConfig.EnableService = true + return nil + } +} + +// AutoNATServiceRateLimit changes the default rate limiting configured in helping +// other peers determine their reachability status. When set, the host will limit +// the number of requests it responds to in each 60 second period to the set +// numbers. A value of '0' disables throttling. +func AutoNATServiceRateLimit(global, perPeer int, interval time.Duration) Option { + return func(cfg *Config) error { + cfg.AutoNATConfig.ThrottleGlobalLimit = global + cfg.AutoNATConfig.ThrottlePeerLimit = perPeer + cfg.AutoNATConfig.ThrottleInterval = interval + return nil + } +} + +// ConnectionGater configures libp2p to use the given ConnectionGater +// to actively reject inbound/outbound connections based on the lifecycle stage +// of the connection. +// +// For more information, refer to go-libp2p/core.ConnectionGater. +func ConnectionGater(cg connmgr.ConnectionGater) Option { + return func(cfg *Config) error { + if cfg.ConnectionGater != nil { + return errors.New("cannot configure multiple connection gaters, or cannot configure both Filters and ConnectionGater") + } + cfg.ConnectionGater = cg + return nil + } +} + +// ResourceManager configures libp2p to use the given ResourceManager. +// When using the p2p/host/resource-manager implementation of the ResourceManager interface, +// it is recommended to set limits for libp2p protocol by calling SetDefaultServiceLimits. +func ResourceManager(rcmgr network.ResourceManager) Option { + return func(cfg *Config) error { + if cfg.ResourceManager != nil { + return errors.New("cannot configure multiple resource managers") + } + cfg.ResourceManager = rcmgr + return nil + } +} + +// NATPortMap configures libp2p to use the default NATManager. The default +// NATManager will attempt to open a port in your network's firewall using UPnP. +func NATPortMap() Option { + return NATManager(bhost.NewNATManager) +} + +// NATManager will configure libp2p to use the requested NATManager. This +// function should be passed a NATManager *constructor* that takes a libp2p Network. +func NATManager(nm config.NATManagerC) Option { + return func(cfg *Config) error { + if cfg.NATManager != nil { + return fmt.Errorf("cannot specify multiple NATManagers") + } + cfg.NATManager = nm + return nil + } +} + +// Ping will configure libp2p to support the ping service; enable by default. +func Ping(enable bool) Option { + return func(cfg *Config) error { + cfg.DisablePing = !enable + return nil + } +} + +// Routing will configure libp2p to use routing. +func Routing(rt config.RoutingC) Option { + return func(cfg *Config) error { + if cfg.Routing != nil { + return fmt.Errorf("cannot specify multiple routing options") + } + cfg.Routing = rt + return nil + } +} + +// NoListenAddrs will configure libp2p to not listen by default. +// +// This will both clear any configured listen addrs and prevent libp2p from +// applying the default listen address option. It also disables relay, unless the +// user explicitly specifies with an option, as the transport creates an implicit +// listen address that would make the node dialable through any relay it was connected to. +var NoListenAddrs = func(cfg *Config) error { + cfg.ListenAddrs = []ma.Multiaddr{} + if !cfg.RelayCustom { + cfg.RelayCustom = true + cfg.Relay = false + } + return nil +} + +// NoTransports will configure libp2p to not enable any transports. +// +// This will both clear any configured transports (specified in prior libp2p +// options) and prevent libp2p from applying the default transports. +var NoTransports = func(cfg *Config) error { + cfg.Transports = []fx.Option{} + return nil +} + +// ProtocolVersion sets the protocolVersion string required by the +// libp2p Identify protocol. +func ProtocolVersion(s string) Option { + return func(cfg *Config) error { + cfg.ProtocolVersion = s + return nil + } +} + +// UserAgent sets the libp2p user-agent sent along with the identify protocol +func UserAgent(userAgent string) Option { + return func(cfg *Config) error { + cfg.UserAgent = userAgent + return nil + } +} + +// MultiaddrResolver sets the libp2p dns resolver +func MultiaddrResolver(rslv network.MultiaddrDNSResolver) Option { + return func(cfg *Config) error { + cfg.MultiaddrResolver = rslv + return nil + } +} + +// Experimental +// EnableHolePunching enables NAT traversal by enabling NATT'd peers to both initiate and respond to hole punching attempts +// to create direct/NAT-traversed connections with other peers. (default: disabled) +// +// Dependencies: +// - Relay (enabled by default) +// +// This subsystem performs two functions: +// +// 1. On receiving an inbound Relay connection, it attempts to create a direct connection with the remote peer +// by initiating and co-ordinating a hole punch over the Relayed connection. +// 2. If a peer sees a request to co-ordinate a hole punch on an outbound Relay connection, +// it will participate in the hole-punch to create a direct connection with the remote peer. +// +// If the hole punch is successful, all new streams will thereafter be created on the hole-punched connection. +// The Relayed connection will eventually be closed after a grace period. +// +// All existing indefinite long-lived streams on the Relayed connection will have to re-opened on the hole-punched connection by the user. +// Users can make use of the `Connected`/`Disconnected` notifications emitted by the Network for this purpose. +// +// It is not mandatory but nice to also enable the `AutoRelay` option (See `EnableAutoRelay`) +// so the peer can discover and connect to Relay servers if it discovers that it is NATT'd and has private reachability via AutoNAT. +// This will then enable it to advertise Relay addresses which can be used to accept inbound Relay connections to then co-ordinate +// a hole punch. +// +// If `EnableAutoRelay` is configured and the user is confident that the peer has private reachability/is NATT'd, +// the `ForceReachabilityPrivate` option can be configured to short-circuit reachability discovery via AutoNAT +// so the peer can immediately start connecting to Relay servers. +// +// If `EnableAutoRelay` is configured, the `StaticRelays` option can be used to configure a static set of Relay servers +// for `AutoRelay` to connect to so that it does not need to discover Relay servers via Routing. +func EnableHolePunching(opts ...holepunch.Option) Option { + return func(cfg *Config) error { + cfg.EnableHolePunching = true + cfg.HolePunchingOptions = opts + return nil + } +} + +func WithDialTimeout(t time.Duration) Option { + return func(cfg *Config) error { + if t <= 0 { + return errors.New("dial timeout needs to be non-negative") + } + cfg.DialTimeout = t + return nil + } +} + +// DisableMetrics configures libp2p to disable prometheus metrics +func DisableMetrics() Option { + return func(cfg *Config) error { + cfg.DisableMetrics = true + return nil + } +} + +// PrometheusRegisterer configures libp2p to use reg as the Registerer for all metrics subsystems +func PrometheusRegisterer(reg prometheus.Registerer) Option { + return func(cfg *Config) error { + if cfg.DisableMetrics { + return errors.New("cannot set registerer when metrics are disabled") + } + if cfg.PrometheusRegisterer != nil { + return errors.New("registerer already set") + } + if reg == nil { + return errors.New("registerer cannot be nil") + } + cfg.PrometheusRegisterer = reg + return nil + } +} + +// DialRanker configures libp2p to use d as the dial ranker. To enable smart +// dialing use `swarm.DefaultDialRanker`. use `swarm.NoDelayDialRanker` to +// disable smart dialing. +// +// Deprecated: use SwarmOpts(swarm.WithDialRanker(d)) instead +func DialRanker(d network.DialRanker) Option { + return func(cfg *Config) error { + if cfg.DialRanker != nil { + return errors.New("dial ranker already configured") + } + cfg.DialRanker = d + return nil + } +} + +// SwarmOpts configures libp2p to use swarm with opts +func SwarmOpts(opts ...swarm.Option) Option { + return func(cfg *Config) error { + cfg.SwarmOpts = opts + return nil + } +} + +// DisableIdentifyAddressDiscovery disables address discovery using peer provided observed addresses +// in identify. If you know your public addresses upfront, the recommended way is to use +// AddressFactory to provide the external adddress to the host and use this option to disable +// discovery from identify. +func DisableIdentifyAddressDiscovery() Option { + return func(cfg *Config) error { + cfg.DisableIdentifyAddressDiscovery = true + return nil + } +} + +// EnableAutoNATv2 enables autonat v2 +func EnableAutoNATv2() Option { + return func(cfg *Config) error { + cfg.EnableAutoNATv2 = true + return nil + } +} + +// UDPBlackHoleSuccessCounter configures libp2p to use f as the black hole filter for UDP addrs +func UDPBlackHoleSuccessCounter(f *swarm.BlackHoleSuccessCounter) Option { + return func(cfg *Config) error { + cfg.UDPBlackHoleSuccessCounter = f + cfg.CustomUDPBlackHoleSuccessCounter = true + return nil + } +} + +// IPv6BlackHoleSuccessCounter configures libp2p to use f as the black hole filter for IPv6 addrs +func IPv6BlackHoleSuccessCounter(f *swarm.BlackHoleSuccessCounter) Option { + return func(cfg *Config) error { + cfg.IPv6BlackHoleSuccessCounter = f + cfg.CustomIPv6BlackHoleSuccessCounter = true + return nil + } +} + +// WithFxOption adds a user provided fx.Option to the libp2p constructor. +// Experimental: This option is subject to change or removal. +func WithFxOption(opts ...fx.Option) Option { + return func(cfg *Config) error { + cfg.UserFxOptions = append(cfg.UserFxOptions, opts...) + return nil + } +} + +// ShareTCPListener shares the same listen address between TCP and Websocket +// transports. This lets both transports use the same TCP port. +// +// Currently this behavior is Opt-in. In a future release this will be the +// default, and this option will be removed. +func ShareTCPListener() Option { + return func(cfg *Config) error { + cfg.ShareTCPListener = true + return nil + } +} diff --git a/p2p/host/basic/basic_host_test.go b/p2p/host/basic/basic_host_test.go index 80a1d3dd62..d8cb6cbf9a 100644 --- a/p2p/host/basic/basic_host_test.go +++ b/p2p/host/basic/basic_host_test.go @@ -1,3 +1,6 @@ +//go:build !js +// +build !js + package basichost import ( diff --git a/p2p/host/basic/natmgr_test.go b/p2p/host/basic/natmgr_test.go index c216e4f6d1..541b84120e 100644 --- a/p2p/host/basic/natmgr_test.go +++ b/p2p/host/basic/natmgr_test.go @@ -1,3 +1,6 @@ +//go:build !js +// +build !js + package basichost import ( diff --git a/p2p/host/pstoremanager/pstoremanager_test.go b/p2p/host/pstoremanager/pstoremanager_test.go index 9321031c5a..2b764f8072 100644 --- a/p2p/host/pstoremanager/pstoremanager_test.go +++ b/p2p/host/pstoremanager/pstoremanager_test.go @@ -1,3 +1,6 @@ +//go:build !js +// +build !js + package pstoremanager_test import ( diff --git a/p2p/http/libp2phttp_test.go b/p2p/http/libp2phttp_test.go index c87c9a3aa7..e59001e2dc 100644 --- a/p2p/http/libp2phttp_test.go +++ b/p2p/http/libp2phttp_test.go @@ -1,3 +1,6 @@ +//go:build !js +// +build !js + package libp2phttp_test import ( diff --git a/p2p/net/connmgr/bench_test.go b/p2p/net/connmgr/bench_test.go index 83442f9162..77507d9b64 100644 --- a/p2p/net/connmgr/bench_test.go +++ b/p2p/net/connmgr/bench_test.go @@ -1,3 +1,6 @@ +//go:build !js +// +build !js + package connmgr import ( diff --git a/p2p/net/connmgr/connmgr_test.go b/p2p/net/connmgr/connmgr_test.go index f47557b02c..c1136ac13e 100644 --- a/p2p/net/connmgr/connmgr_test.go +++ b/p2p/net/connmgr/connmgr_test.go @@ -1,3 +1,6 @@ +//go:build !js +// +build !js + package connmgr import ( diff --git a/p2p/net/swarm/dial_test.go b/p2p/net/swarm/dial_test.go index feb9f49174..93e18773a9 100644 --- a/p2p/net/swarm/dial_test.go +++ b/p2p/net/swarm/dial_test.go @@ -1,3 +1,6 @@ +//go:build !js +// +build !js + package swarm_test import ( diff --git a/p2p/net/swarm/dial_worker_test.go b/p2p/net/swarm/dial_worker_test.go index c7298fb61e..5ed22121ee 100644 --- a/p2p/net/swarm/dial_worker_test.go +++ b/p2p/net/swarm/dial_worker_test.go @@ -1,3 +1,6 @@ +//go:build !js +// +build !js + package swarm import ( diff --git a/p2p/net/swarm/peers_test.go b/p2p/net/swarm/peers_test.go index 44f1309f75..34a490bc81 100644 --- a/p2p/net/swarm/peers_test.go +++ b/p2p/net/swarm/peers_test.go @@ -1,3 +1,6 @@ +//go:build !js +// +build !js + package swarm_test import ( diff --git a/p2p/net/swarm/simul_test.go b/p2p/net/swarm/simul_test.go index 33026d0e11..7dcda61ca5 100644 --- a/p2p/net/swarm/simul_test.go +++ b/p2p/net/swarm/simul_test.go @@ -1,3 +1,6 @@ +//go:build !js +// +build !js + package swarm_test import ( diff --git a/p2p/net/swarm/swarm_addr_test.go b/p2p/net/swarm/swarm_addr_test.go index 43e76716e5..91f5e78f2b 100644 --- a/p2p/net/swarm/swarm_addr_test.go +++ b/p2p/net/swarm/swarm_addr_test.go @@ -1,3 +1,6 @@ +//go:build !js +// +build !js + package swarm_test import ( diff --git a/p2p/net/swarm/swarm_dial_test.go b/p2p/net/swarm/swarm_dial_test.go index 6d14730b68..af2f719086 100644 --- a/p2p/net/swarm/swarm_dial_test.go +++ b/p2p/net/swarm/swarm_dial_test.go @@ -1,3 +1,6 @@ +//go:build !js +// +build !js + package swarm import ( diff --git a/p2p/net/swarm/swarm_event_test.go b/p2p/net/swarm/swarm_event_test.go index 5010215fc2..85f698916d 100644 --- a/p2p/net/swarm/swarm_event_test.go +++ b/p2p/net/swarm/swarm_event_test.go @@ -1,3 +1,6 @@ +//go:build !js +// +build !js + package swarm_test import ( diff --git a/p2p/net/swarm/swarm_net_test.go b/p2p/net/swarm/swarm_net_test.go index 1dbdc57bcc..55d67cff93 100644 --- a/p2p/net/swarm/swarm_net_test.go +++ b/p2p/net/swarm/swarm_net_test.go @@ -1,3 +1,6 @@ +//go:build !js +// +build !js + package swarm_test import ( diff --git a/p2p/net/swarm/swarm_notif_test.go b/p2p/net/swarm/swarm_notif_test.go index 0b6d56122e..a51c4e284c 100644 --- a/p2p/net/swarm/swarm_notif_test.go +++ b/p2p/net/swarm/swarm_notif_test.go @@ -1,3 +1,6 @@ +//go:build !js +// +build !js + package swarm_test import ( diff --git a/p2p/net/swarm/swarm_test.go b/p2p/net/swarm/swarm_test.go index 496236f826..9e2527fc66 100644 --- a/p2p/net/swarm/swarm_test.go +++ b/p2p/net/swarm/swarm_test.go @@ -1,3 +1,6 @@ +//go:build !js +// +build !js + package swarm_test import ( diff --git a/p2p/net/swarm/testing/testing.go b/p2p/net/swarm/testing/testing.go index 773314a1b8..fde27b64e5 100644 --- a/p2p/net/swarm/testing/testing.go +++ b/p2p/net/swarm/testing/testing.go @@ -1,3 +1,6 @@ +//go:build !js +// +build !js + package testing import ( diff --git a/p2p/net/swarm/testing/testing_test.go b/p2p/net/swarm/testing/testing_test.go index d4a43dfb59..04d6eb2f33 100644 --- a/p2p/net/swarm/testing/testing_test.go +++ b/p2p/net/swarm/testing/testing_test.go @@ -1,3 +1,6 @@ +//go:build !js +// +build !js + package testing import ( diff --git a/p2p/net/swarm/transport_test.go b/p2p/net/swarm/transport_test.go index fe7434753e..0ed28e4760 100644 --- a/p2p/net/swarm/transport_test.go +++ b/p2p/net/swarm/transport_test.go @@ -1,3 +1,6 @@ +//go:build !js +// +build !js + package swarm_test import ( diff --git a/p2p/protocol/autonatv2/autonat_test.go b/p2p/protocol/autonatv2/autonat_test.go index 11c8f02195..702d340386 100644 --- a/p2p/protocol/autonatv2/autonat_test.go +++ b/p2p/protocol/autonatv2/autonat_test.go @@ -1,3 +1,6 @@ +//go:build !js +// +build !js + package autonatv2 import ( diff --git a/p2p/protocol/autonatv2/server_test.go b/p2p/protocol/autonatv2/server_test.go index c65aa5b880..f0c605ed5c 100644 --- a/p2p/protocol/autonatv2/server_test.go +++ b/p2p/protocol/autonatv2/server_test.go @@ -1,3 +1,6 @@ +//go:build !js +// +build !js + package autonatv2 import ( diff --git a/p2p/protocol/circuitv2/relay/relay_test.go b/p2p/protocol/circuitv2/relay/relay_test.go index 7c5ec927df..207ba49d5b 100644 --- a/p2p/protocol/circuitv2/relay/relay_test.go +++ b/p2p/protocol/circuitv2/relay/relay_test.go @@ -1,3 +1,6 @@ +//go:build !js +// +build !js + package relay_test import ( diff --git a/p2p/protocol/identify/id_glass_test.go b/p2p/protocol/identify/id_glass_test.go index 3eec26cb75..287e84b759 100644 --- a/p2p/protocol/identify/id_glass_test.go +++ b/p2p/protocol/identify/id_glass_test.go @@ -1,3 +1,6 @@ +//go:build !js +// +build !js + package identify import ( diff --git a/p2p/protocol/identify/id_test.go b/p2p/protocol/identify/id_test.go index 34d6c240f6..39d5d7e5c7 100644 --- a/p2p/protocol/identify/id_test.go +++ b/p2p/protocol/identify/id_test.go @@ -1,3 +1,6 @@ +//go:build !js +// +build !js + package identify_test import ( diff --git a/p2p/protocol/identify/obsaddr_test.go b/p2p/protocol/identify/obsaddr_test.go index aad55bb375..2f05bf18ad 100644 --- a/p2p/protocol/identify/obsaddr_test.go +++ b/p2p/protocol/identify/obsaddr_test.go @@ -1,3 +1,6 @@ +//go:build !js +// +build !js + package identify import ( diff --git a/p2p/protocol/ping/ping_test.go b/p2p/protocol/ping/ping_test.go index d33662ee91..7490ba4773 100644 --- a/p2p/protocol/ping/ping_test.go +++ b/p2p/protocol/ping/ping_test.go @@ -1,3 +1,6 @@ +//go:build !js +// +build !js + package ping_test import ( diff --git a/p2p/security/tls/cmd/tlsdiag.go b/p2p/security/tls/cmd/tlsdiag.go index d6f7bac674..360009858f 100644 --- a/p2p/security/tls/cmd/tlsdiag.go +++ b/p2p/security/tls/cmd/tlsdiag.go @@ -1,3 +1,6 @@ +//go:build !js +// +build !js + package main import ( diff --git a/p2p/security/tls/cmd/tlsdiag/client.go b/p2p/security/tls/cmd/tlsdiag/client.go index a29189a375..ba6932dfd4 100644 --- a/p2p/security/tls/cmd/tlsdiag/client.go +++ b/p2p/security/tls/cmd/tlsdiag/client.go @@ -1,3 +1,6 @@ +//go:build !js +// +build !js + package tlsdiag import ( diff --git a/p2p/security/tls/cmd/tlsdiag/server.go b/p2p/security/tls/cmd/tlsdiag/server.go index cd702a7334..efbdddafd8 100644 --- a/p2p/security/tls/cmd/tlsdiag/server.go +++ b/p2p/security/tls/cmd/tlsdiag/server.go @@ -1,3 +1,6 @@ +//go:build !js +// +build !js + package tlsdiag import ( diff --git a/p2p/security/tls/crypto.go b/p2p/security/tls/crypto.go index 70a594d060..157cc7d4f0 100644 --- a/p2p/security/tls/crypto.go +++ b/p2p/security/tls/crypto.go @@ -1,3 +1,6 @@ +//go:build !js +// +build !js + package libp2ptls import ( diff --git a/p2p/security/tls/crypto_test.go b/p2p/security/tls/crypto_test.go index 6171d178cd..be9da2ccf9 100644 --- a/p2p/security/tls/crypto_test.go +++ b/p2p/security/tls/crypto_test.go @@ -1,3 +1,6 @@ +//go:build !js +// +build !js + package libp2ptls import ( diff --git a/p2p/security/tls/transport.go b/p2p/security/tls/transport.go index 0c494a7fdc..3358a0d94c 100644 --- a/p2p/security/tls/transport.go +++ b/p2p/security/tls/transport.go @@ -1,3 +1,6 @@ +//go:build !js +// +build !js + package libp2ptls import ( diff --git a/p2p/security/tls/transport_test.go b/p2p/security/tls/transport_test.go index b53d9bf0f6..953e008f87 100644 --- a/p2p/security/tls/transport_test.go +++ b/p2p/security/tls/transport_test.go @@ -1,3 +1,6 @@ +//go:build !js +// +build !js + package libp2ptls import ( diff --git a/p2p/test/backpressure/backpressure_test.go b/p2p/test/backpressure/backpressure_test.go index 207171a78e..4302c37a1b 100644 --- a/p2p/test/backpressure/backpressure_test.go +++ b/p2p/test/backpressure/backpressure_test.go @@ -1,3 +1,6 @@ +//go:build !js +// +build !js + package backpressure_tests import ( diff --git a/p2p/test/basichost/basic_host_test.go b/p2p/test/basichost/basic_host_test.go index 0197387b1b..932417f494 100644 --- a/p2p/test/basichost/basic_host_test.go +++ b/p2p/test/basichost/basic_host_test.go @@ -1,3 +1,6 @@ +//go:build !js +// +build !js + package basichost import ( diff --git a/p2p/test/notifications/notification_test.go b/p2p/test/notifications/notification_test.go index eb26a3fe5e..235e7657a9 100644 --- a/p2p/test/notifications/notification_test.go +++ b/p2p/test/notifications/notification_test.go @@ -1,3 +1,6 @@ +//go:build !js +// +build !js + package notifications import ( diff --git a/p2p/test/quic/quic_test.go b/p2p/test/quic/quic_test.go index fe52119b89..37d406a0cb 100644 --- a/p2p/test/quic/quic_test.go +++ b/p2p/test/quic/quic_test.go @@ -1,3 +1,6 @@ +//go:build !js +// +build !js + package quic_test import ( diff --git a/p2p/test/reconnects/reconnect_test.go b/p2p/test/reconnects/reconnect_test.go index cf05c80f37..8dbd43a2e6 100644 --- a/p2p/test/reconnects/reconnect_test.go +++ b/p2p/test/reconnects/reconnect_test.go @@ -1,3 +1,6 @@ +//go:build !js +// +build !js + package reconnect import ( diff --git a/p2p/test/transport/deadline_test.go b/p2p/test/transport/deadline_test.go index 55fa7a4fbc..df174a34cf 100644 --- a/p2p/test/transport/deadline_test.go +++ b/p2p/test/transport/deadline_test.go @@ -1,3 +1,6 @@ +//go:build !js +// +build !js + package transport_integration import ( diff --git a/p2p/test/transport/gating_test.go b/p2p/test/transport/gating_test.go index c9ae4fd80d..73a0e5cb72 100644 --- a/p2p/test/transport/gating_test.go +++ b/p2p/test/transport/gating_test.go @@ -1,3 +1,6 @@ +//go:build !js +// +build !js + package transport_integration import ( diff --git a/p2p/test/transport/mock_connection_gater_test.go b/p2p/test/transport/mock_connection_gater_test.go index 5e13863f54..22d62db369 100644 --- a/p2p/test/transport/mock_connection_gater_test.go +++ b/p2p/test/transport/mock_connection_gater_test.go @@ -1,3 +1,5 @@ +//go:build !js +// +build !js // Code generated by MockGen. DO NOT EDIT. // Source: github.com/libp2p/go-libp2p/core/connmgr (interfaces: ConnectionGater) // diff --git a/p2p/test/transport/rcmgr_test.go b/p2p/test/transport/rcmgr_test.go index 9a6a09b87d..9cdc97dccc 100644 --- a/p2p/test/transport/rcmgr_test.go +++ b/p2p/test/transport/rcmgr_test.go @@ -1,3 +1,6 @@ +//go:build !js +// +build !js + package transport_integration import ( diff --git a/p2p/test/transport/transport_test.go b/p2p/test/transport/transport_test.go index c8445a3997..978fba1ca3 100644 --- a/p2p/test/transport/transport_test.go +++ b/p2p/test/transport/transport_test.go @@ -1,3 +1,6 @@ +//go:build !js +// +build !js + package transport_integration import ( diff --git a/p2p/test/webtransport/webtransport_test.go b/p2p/test/webtransport/webtransport_test.go index e9c612baac..7f52d724cb 100644 --- a/p2p/test/webtransport/webtransport_test.go +++ b/p2p/test/webtransport/webtransport_test.go @@ -1,3 +1,6 @@ +//go:build !js +// +build !js + package webtransport_test import ( diff --git a/p2p/transport/quic/cmd/client/main.go b/p2p/transport/quic/cmd/client/main.go index e9883d2a1d..04369c1f31 100644 --- a/p2p/transport/quic/cmd/client/main.go +++ b/p2p/transport/quic/cmd/client/main.go @@ -1,3 +1,6 @@ +//go:build !js +// +build !js + package main import ( diff --git a/p2p/transport/quic/cmd/lib/lib.go b/p2p/transport/quic/cmd/lib/lib.go index fd2a270af0..29a7c40dac 100644 --- a/p2p/transport/quic/cmd/lib/lib.go +++ b/p2p/transport/quic/cmd/lib/lib.go @@ -1,3 +1,6 @@ +//go:build !js +// +build !js + package cmdlib import ( diff --git a/p2p/transport/quic/cmd/lib/lib_test.go b/p2p/transport/quic/cmd/lib/lib_test.go index 2175874322..9bc8586033 100644 --- a/p2p/transport/quic/cmd/lib/lib_test.go +++ b/p2p/transport/quic/cmd/lib/lib_test.go @@ -1,3 +1,6 @@ +//go:build !js +// +build !js + package cmdlib import ( diff --git a/p2p/transport/quic/cmd/server/main.go b/p2p/transport/quic/cmd/server/main.go index c478d34b22..24068590c9 100644 --- a/p2p/transport/quic/cmd/server/main.go +++ b/p2p/transport/quic/cmd/server/main.go @@ -1,3 +1,6 @@ +//go:build !js +// +build !js + package main import ( diff --git a/p2p/transport/quic/conn.go b/p2p/transport/quic/conn.go index 8b381d8eda..6c682ce61b 100644 --- a/p2p/transport/quic/conn.go +++ b/p2p/transport/quic/conn.go @@ -1,3 +1,6 @@ +//go:build !js +// +build !js + package libp2pquic import ( diff --git a/p2p/transport/quic/conn_test.go b/p2p/transport/quic/conn_test.go index 83300eb978..e0de4deb76 100644 --- a/p2p/transport/quic/conn_test.go +++ b/p2p/transport/quic/conn_test.go @@ -1,3 +1,6 @@ +//go:build !js +// +build !js + package libp2pquic import ( diff --git a/p2p/transport/quic/listener.go b/p2p/transport/quic/listener.go index 30868e49eb..b21db4c39b 100644 --- a/p2p/transport/quic/listener.go +++ b/p2p/transport/quic/listener.go @@ -1,3 +1,6 @@ +//go:build !js +// +build !js + package libp2pquic import ( diff --git a/p2p/transport/quic/listener_test.go b/p2p/transport/quic/listener_test.go index 53d6001d35..a23114c5be 100644 --- a/p2p/transport/quic/listener_test.go +++ b/p2p/transport/quic/listener_test.go @@ -1,3 +1,6 @@ +//go:build !js +// +build !js + package libp2pquic import ( diff --git a/p2p/transport/quic/stream.go b/p2p/transport/quic/stream.go index 1de4770dce..d6d9174364 100644 --- a/p2p/transport/quic/stream.go +++ b/p2p/transport/quic/stream.go @@ -1,3 +1,6 @@ +//go:build !js +// +build !js + package libp2pquic import ( diff --git a/p2p/transport/quic/transport.go b/p2p/transport/quic/transport.go index 62d31a8d2a..ce47a9d176 100644 --- a/p2p/transport/quic/transport.go +++ b/p2p/transport/quic/transport.go @@ -1,3 +1,6 @@ +//go:build !js +// +build !js + package libp2pquic import ( diff --git a/p2p/transport/quic/transport_test.go b/p2p/transport/quic/transport_test.go index 41e7e4e416..47be1279c0 100644 --- a/p2p/transport/quic/transport_test.go +++ b/p2p/transport/quic/transport_test.go @@ -1,3 +1,6 @@ +//go:build !js +// +build !js + package libp2pquic import ( diff --git a/p2p/transport/quic/virtuallistener.go b/p2p/transport/quic/virtuallistener.go index 5b23e4c507..3deaa97b34 100644 --- a/p2p/transport/quic/virtuallistener.go +++ b/p2p/transport/quic/virtuallistener.go @@ -1,3 +1,6 @@ +//go:build !js +// +build !js + package libp2pquic import ( diff --git a/p2p/transport/quicreuse/config.go b/p2p/transport/quicreuse/config.go index 62f8919c8b..47f9b6bbf8 100644 --- a/p2p/transport/quicreuse/config.go +++ b/p2p/transport/quicreuse/config.go @@ -1,3 +1,6 @@ +//go:build !js +// +build !js + package quicreuse import ( diff --git a/p2p/transport/quicreuse/connmgr.go b/p2p/transport/quicreuse/connmgr.go index c9e3088b5e..28465f5b2b 100644 --- a/p2p/transport/quicreuse/connmgr.go +++ b/p2p/transport/quicreuse/connmgr.go @@ -1,3 +1,6 @@ +//go:build !js +// +build !js + package quicreuse import ( diff --git a/p2p/transport/quicreuse/connmgr_test.go b/p2p/transport/quicreuse/connmgr_test.go index d128119dab..2185514ce8 100644 --- a/p2p/transport/quicreuse/connmgr_test.go +++ b/p2p/transport/quicreuse/connmgr_test.go @@ -1,3 +1,6 @@ +//go:build !js +// +build !js + package quicreuse import ( diff --git a/p2p/transport/quicreuse/listener.go b/p2p/transport/quicreuse/listener.go index 44028197d8..552af8f4bb 100644 --- a/p2p/transport/quicreuse/listener.go +++ b/p2p/transport/quicreuse/listener.go @@ -1,3 +1,6 @@ +//go:build !js +// +build !js + package quicreuse import ( diff --git a/p2p/transport/quicreuse/nonquic_packetconn.go b/p2p/transport/quicreuse/nonquic_packetconn.go index 833bd5804a..ce86803cf8 100644 --- a/p2p/transport/quicreuse/nonquic_packetconn.go +++ b/p2p/transport/quicreuse/nonquic_packetconn.go @@ -1,3 +1,6 @@ +//go:build !js +// +build !js + package quicreuse import ( diff --git a/p2p/transport/quicreuse/options.go b/p2p/transport/quicreuse/options.go index 36a7891393..af20128934 100644 --- a/p2p/transport/quicreuse/options.go +++ b/p2p/transport/quicreuse/options.go @@ -1,3 +1,6 @@ +//go:build !js +// +build !js + package quicreuse import ( diff --git a/p2p/transport/quicreuse/quic_multiaddr.go b/p2p/transport/quicreuse/quic_multiaddr.go index af16547357..308002341c 100644 --- a/p2p/transport/quicreuse/quic_multiaddr.go +++ b/p2p/transport/quicreuse/quic_multiaddr.go @@ -1,3 +1,6 @@ +//go:build !js +// +build !js + package quicreuse import ( diff --git a/p2p/transport/quicreuse/quic_multiaddr_test.go b/p2p/transport/quicreuse/quic_multiaddr_test.go index a6242e2674..1db63724c3 100644 --- a/p2p/transport/quicreuse/quic_multiaddr_test.go +++ b/p2p/transport/quicreuse/quic_multiaddr_test.go @@ -1,3 +1,6 @@ +//go:build !js +// +build !js + package quicreuse import ( diff --git a/p2p/transport/quicreuse/reuse.go b/p2p/transport/quicreuse/reuse.go index 6d0098e33d..f177293418 100644 --- a/p2p/transport/quicreuse/reuse.go +++ b/p2p/transport/quicreuse/reuse.go @@ -1,3 +1,6 @@ +//go:build !js +// +build !js + package quicreuse import ( diff --git a/p2p/transport/quicreuse/reuse_test.go b/p2p/transport/quicreuse/reuse_test.go index ad08c75b5d..9ec4d863e3 100644 --- a/p2p/transport/quicreuse/reuse_test.go +++ b/p2p/transport/quicreuse/reuse_test.go @@ -1,3 +1,6 @@ +//go:build !js +// +build !js + package quicreuse import ( diff --git a/p2p/transport/quicreuse/tracer.go b/p2p/transport/quicreuse/tracer.go index 16c7dce4f1..2334e50b2a 100644 --- a/p2p/transport/quicreuse/tracer.go +++ b/p2p/transport/quicreuse/tracer.go @@ -1,3 +1,6 @@ +//go:build !js +// +build !js + package quicreuse import ( diff --git a/p2p/transport/quicreuse/tracer_test.go b/p2p/transport/quicreuse/tracer_test.go index bea6b91dbf..7175dffcd7 100644 --- a/p2p/transport/quicreuse/tracer_test.go +++ b/p2p/transport/quicreuse/tracer_test.go @@ -1,3 +1,6 @@ +//go:build !js +// +build !js + package quicreuse import ( diff --git a/p2p/transport/tcp/metrics.go b/p2p/transport/tcp/metrics.go index e3946cf348..bb68a11585 100644 --- a/p2p/transport/tcp/metrics.go +++ b/p2p/transport/tcp/metrics.go @@ -1,4 +1,4 @@ -//go:build !windows && !riscv64 && !loong64 +//go:build !js && !windows && !riscv64 && !loong64 package tcp diff --git a/p2p/transport/tcp/metrics_general.go b/p2p/transport/tcp/metrics_general.go index 419fff360e..34285a57c2 100644 --- a/p2p/transport/tcp/metrics_general.go +++ b/p2p/transport/tcp/metrics_general.go @@ -1,4 +1,4 @@ -//go:build !linux && !darwin && !windows && !riscv64 && !loong64 +//go:build !linux && !darwin && !windows && !riscv64 && !loong64 && !js package tcp diff --git a/p2p/transport/tcp/metrics_none.go b/p2p/transport/tcp/metrics_none.go index 2e561fb6cb..8a2e96cbc6 100644 --- a/p2p/transport/tcp/metrics_none.go +++ b/p2p/transport/tcp/metrics_none.go @@ -1,6 +1,6 @@ // riscv64 see: https://github.com/marten-seemann/tcp/pull/1 -//go:build windows || riscv64 || loong64 +//go:build windows || riscv64 || loong64 || js package tcp diff --git a/p2p/transport/tcp/metrics_test.go b/p2p/transport/tcp/metrics_test.go index 7645abc8df..5cd2f977b6 100644 --- a/p2p/transport/tcp/metrics_test.go +++ b/p2p/transport/tcp/metrics_test.go @@ -1,3 +1,6 @@ +//go:build !js +// +build !js + package tcp import ( diff --git a/p2p/transport/tcp/tcp.go b/p2p/transport/tcp/tcp.go index 0b0980c96e..be6a991e18 100644 --- a/p2p/transport/tcp/tcp.go +++ b/p2p/transport/tcp/tcp.go @@ -1,3 +1,6 @@ +//go:build !js +// +build !js + package tcp import ( diff --git a/p2p/transport/tcp/tcp_test.go b/p2p/transport/tcp/tcp_test.go index 1a712acf35..db1b8c814c 100644 --- a/p2p/transport/tcp/tcp_test.go +++ b/p2p/transport/tcp/tcp_test.go @@ -1,3 +1,6 @@ +//go:build !js +// +build !js + package tcp import ( diff --git a/p2p/transport/tcpreuse/connwithscope.go b/p2p/transport/tcpreuse/connwithscope.go index 23354b81cd..f9d6fffef8 100644 --- a/p2p/transport/tcpreuse/connwithscope.go +++ b/p2p/transport/tcpreuse/connwithscope.go @@ -1,3 +1,6 @@ +//go:build !js +// +build !js + package tcpreuse import ( diff --git a/p2p/transport/tcpreuse/demultiplex.go b/p2p/transport/tcpreuse/demultiplex.go index f9175ecfdb..48530854ed 100644 --- a/p2p/transport/tcpreuse/demultiplex.go +++ b/p2p/transport/tcpreuse/demultiplex.go @@ -1,3 +1,6 @@ +//go:build !js +// +build !js + package tcpreuse import ( diff --git a/p2p/transport/tcpreuse/demultiplex_test.go b/p2p/transport/tcpreuse/demultiplex_test.go index e201f2ca75..5455146209 100644 --- a/p2p/transport/tcpreuse/demultiplex_test.go +++ b/p2p/transport/tcpreuse/demultiplex_test.go @@ -1,3 +1,6 @@ +//go:build !js +// +build !js + package tcpreuse import "testing" diff --git a/p2p/transport/tcpreuse/dialer.go b/p2p/transport/tcpreuse/dialer.go index d6ea1fc6a0..5511acc742 100644 --- a/p2p/transport/tcpreuse/dialer.go +++ b/p2p/transport/tcpreuse/dialer.go @@ -1,3 +1,6 @@ +//go:build !js +// +build !js + package tcpreuse import ( diff --git a/p2p/transport/tcpreuse/internal/sampledconn/sampledconn.go b/p2p/transport/tcpreuse/internal/sampledconn/sampledconn.go index ff1f8caf44..5c0f081e02 100644 --- a/p2p/transport/tcpreuse/internal/sampledconn/sampledconn.go +++ b/p2p/transport/tcpreuse/internal/sampledconn/sampledconn.go @@ -1,3 +1,6 @@ +//go:build !js +// +build !js + package sampledconn import ( diff --git a/p2p/transport/tcpreuse/internal/sampledconn/sampledconn_test.go b/p2p/transport/tcpreuse/internal/sampledconn/sampledconn_test.go index 6c4e989b16..0898b7d6d3 100644 --- a/p2p/transport/tcpreuse/internal/sampledconn/sampledconn_test.go +++ b/p2p/transport/tcpreuse/internal/sampledconn/sampledconn_test.go @@ -1,3 +1,6 @@ +//go:build !js +// +build !js + package sampledconn import ( diff --git a/p2p/transport/tcpreuse/listener.go b/p2p/transport/tcpreuse/listener.go index 4c787acc3e..5cdf327c43 100644 --- a/p2p/transport/tcpreuse/listener.go +++ b/p2p/transport/tcpreuse/listener.go @@ -1,3 +1,6 @@ +//go:build !js +// +build !js + package tcpreuse import ( diff --git a/p2p/transport/tcpreuse/listener_test.go b/p2p/transport/tcpreuse/listener_test.go index 0f91d4992d..bc72783c10 100644 --- a/p2p/transport/tcpreuse/listener_test.go +++ b/p2p/transport/tcpreuse/listener_test.go @@ -1,3 +1,6 @@ +//go:build !js +// +build !js + package tcpreuse import ( diff --git a/p2p/transport/tcpreuse/reuseport.go b/p2p/transport/tcpreuse/reuseport.go index a2529c0bda..70b40a9b02 100644 --- a/p2p/transport/tcpreuse/reuseport.go +++ b/p2p/transport/tcpreuse/reuseport.go @@ -1,3 +1,6 @@ +//go:build !js +// +build !js + package tcpreuse import ( diff --git a/p2p/transport/webrtc/connection.go b/p2p/transport/webrtc/connection.go index d75c309c51..3b8f584bfa 100644 --- a/p2p/transport/webrtc/connection.go +++ b/p2p/transport/webrtc/connection.go @@ -1,3 +1,6 @@ +//go:build !js +// +build !js + package libp2pwebrtc import ( diff --git a/p2p/transport/webrtc/fingerprint.go b/p2p/transport/webrtc/fingerprint.go index 0b1fe488b4..1cb846d208 100644 --- a/p2p/transport/webrtc/fingerprint.go +++ b/p2p/transport/webrtc/fingerprint.go @@ -1,3 +1,6 @@ +//go:build !js +// +build !js + package libp2pwebrtc import ( diff --git a/p2p/transport/webrtc/hex_test.go b/p2p/transport/webrtc/hex_test.go index c8a7147498..4cd43b63bd 100644 --- a/p2p/transport/webrtc/hex_test.go +++ b/p2p/transport/webrtc/hex_test.go @@ -1,3 +1,6 @@ +//go:build !js +// +build !js + package libp2pwebrtc import ( diff --git a/p2p/transport/webrtc/listener.go b/p2p/transport/webrtc/listener.go index 0ec05ec0e9..0814bc385e 100644 --- a/p2p/transport/webrtc/listener.go +++ b/p2p/transport/webrtc/listener.go @@ -1,3 +1,6 @@ +//go:build !js +// +build !js + package libp2pwebrtc import ( diff --git a/p2p/transport/webrtc/logger.go b/p2p/transport/webrtc/logger.go index ebe5fa309f..ae86cb4a6d 100644 --- a/p2p/transport/webrtc/logger.go +++ b/p2p/transport/webrtc/logger.go @@ -1,3 +1,6 @@ +//go:build !js +// +build !js + package libp2pwebrtc import ( diff --git a/p2p/transport/webrtc/sdp.go b/p2p/transport/webrtc/sdp.go index 878b668a18..1856f172ea 100644 --- a/p2p/transport/webrtc/sdp.go +++ b/p2p/transport/webrtc/sdp.go @@ -1,3 +1,6 @@ +//go:build !js +// +build !js + package libp2pwebrtc import ( diff --git a/p2p/transport/webrtc/sdp_test.go b/p2p/transport/webrtc/sdp_test.go index 2e7ac5b3e8..bc573cb2c1 100644 --- a/p2p/transport/webrtc/sdp_test.go +++ b/p2p/transport/webrtc/sdp_test.go @@ -1,3 +1,6 @@ +//go:build !js +// +build !js + package libp2pwebrtc import ( diff --git a/p2p/transport/webrtc/stream.go b/p2p/transport/webrtc/stream.go index c92457ccb0..a05f0b8bc5 100644 --- a/p2p/transport/webrtc/stream.go +++ b/p2p/transport/webrtc/stream.go @@ -1,3 +1,6 @@ +//go:build !js +// +build !js + package libp2pwebrtc import ( diff --git a/p2p/transport/webrtc/stream_read.go b/p2p/transport/webrtc/stream_read.go index 003d5f563e..d0289fac6d 100644 --- a/p2p/transport/webrtc/stream_read.go +++ b/p2p/transport/webrtc/stream_read.go @@ -1,3 +1,6 @@ +//go:build !js +// +build !js + package libp2pwebrtc import ( diff --git a/p2p/transport/webrtc/stream_test.go b/p2p/transport/webrtc/stream_test.go index 461ed27ff8..44c48e40e3 100644 --- a/p2p/transport/webrtc/stream_test.go +++ b/p2p/transport/webrtc/stream_test.go @@ -1,3 +1,6 @@ +//go:build !js +// +build !js + package libp2pwebrtc import ( diff --git a/p2p/transport/webrtc/stream_write.go b/p2p/transport/webrtc/stream_write.go index 8629955191..7dc33b0bb0 100644 --- a/p2p/transport/webrtc/stream_write.go +++ b/p2p/transport/webrtc/stream_write.go @@ -1,3 +1,6 @@ +//go:build !js +// +build !js + package libp2pwebrtc import ( diff --git a/p2p/transport/webrtc/transport.go b/p2p/transport/webrtc/transport.go index f3322c8117..68c7c2c13a 100644 --- a/p2p/transport/webrtc/transport.go +++ b/p2p/transport/webrtc/transport.go @@ -1,3 +1,6 @@ +//go:build !js +// +build !js + // Package libp2pwebrtc implements the WebRTC transport for go-libp2p, // as described in https://github.com/libp2p/specs/tree/master/webrtc. package libp2pwebrtc @@ -104,8 +107,6 @@ type iceTimeouts struct { Keepalive time.Duration } -type ListenUDPFn func(network string, laddr *net.UDPAddr) (net.PacketConn, error) - func New(privKey ic.PrivKey, psk pnet.PSK, gater connmgr.ConnectionGater, rcmgr network.ResourceManager, listenUDP ListenUDPFn, opts ...Option) (*WebRTCTransport, error) { if psk != nil { log.Error("WebRTC doesn't support private networks yet.") @@ -638,35 +639,3 @@ func newWebRTCConnection(settings webrtc.SettingEngine, config webrtc.Configurat PeerConnectionClosedCh: connectionClosedCh, }, nil } - -// IsWebRTCDirectMultiaddr returns whether addr is a /webrtc-direct multiaddr with the count of certhashes -// in addr -func IsWebRTCDirectMultiaddr(addr ma.Multiaddr) (bool, int) { - var foundUDP, foundWebRTC bool - certHashCount := 0 - ma.ForEach(addr, func(c ma.Component) bool { - if !foundUDP { - if c.Protocol().Code == ma.P_UDP { - foundUDP = true - } - return true - } - if !foundWebRTC && foundUDP { - // protocol after udp must be webrtc-direct - if c.Protocol().Code != ma.P_WEBRTC_DIRECT { - return false - } - foundWebRTC = true - return true - } - if foundWebRTC { - if c.Protocol().Code == ma.P_CERTHASH { - certHashCount++ - } else { - return false - } - } - return true - }) - return foundUDP && foundWebRTC, certHashCount -} diff --git a/p2p/transport/webrtc/transport_shared.go b/p2p/transport/webrtc/transport_shared.go new file mode 100644 index 0000000000..284256ad0d --- /dev/null +++ b/p2p/transport/webrtc/transport_shared.go @@ -0,0 +1,42 @@ +// as described in https://github.com/libp2p/specs/tree/master/webrtc. +package libp2pwebrtc + +import ( + "net" + + ma "github.com/multiformats/go-multiaddr" +) + +type ListenUDPFn func(network string, laddr *net.UDPAddr) (net.PacketConn, error) + +// IsWebRTCDirectMultiaddr returns whether addr is a /webrtc-direct multiaddr with the count of certhashes +// in addr +func IsWebRTCDirectMultiaddr(addr ma.Multiaddr) (bool, int) { + var foundUDP, foundWebRTC bool + certHashCount := 0 + ma.ForEach(addr, func(c ma.Component) bool { + if !foundUDP { + if c.Protocol().Code == ma.P_UDP { + foundUDP = true + } + return true + } + if !foundWebRTC && foundUDP { + // protocol after udp must be webrtc-direct + if c.Protocol().Code != ma.P_WEBRTC_DIRECT { + return false + } + foundWebRTC = true + return true + } + if foundWebRTC { + if c.Protocol().Code == ma.P_CERTHASH { + certHashCount++ + } else { + return false + } + } + return true + }) + return foundUDP && foundWebRTC, certHashCount +} diff --git a/p2p/transport/webrtc/transport_test.go b/p2p/transport/webrtc/transport_test.go index 83f65c8c3f..94006b62e4 100644 --- a/p2p/transport/webrtc/transport_test.go +++ b/p2p/transport/webrtc/transport_test.go @@ -1,3 +1,6 @@ +//go:build !js +// +build !js + package libp2pwebrtc import ( diff --git a/p2p/transport/webrtc/udpmux/mux.go b/p2p/transport/webrtc/udpmux/mux.go index 76d68d8e89..e5faa63107 100644 --- a/p2p/transport/webrtc/udpmux/mux.go +++ b/p2p/transport/webrtc/udpmux/mux.go @@ -1,3 +1,6 @@ +//go:build !js +// +build !js + // The udpmux package contains the logic for multiplexing multiple WebRTC (ICE) // connections over a single UDP socket. package udpmux diff --git a/p2p/transport/webrtc/udpmux/mux_test.go b/p2p/transport/webrtc/udpmux/mux_test.go index b75f3e8302..fdf04f90c4 100644 --- a/p2p/transport/webrtc/udpmux/mux_test.go +++ b/p2p/transport/webrtc/udpmux/mux_test.go @@ -1,3 +1,6 @@ +//go:build !js +// +build !js + package udpmux import ( diff --git a/p2p/transport/webrtc/udpmux/muxed_connection.go b/p2p/transport/webrtc/udpmux/muxed_connection.go index 84d30c84b1..26117eb644 100644 --- a/p2p/transport/webrtc/udpmux/muxed_connection.go +++ b/p2p/transport/webrtc/udpmux/muxed_connection.go @@ -1,3 +1,6 @@ +//go:build !js +// +build !js + package udpmux import ( diff --git a/p2p/transport/websocket/addrs.go b/p2p/transport/websocket/addrs.go index 6fbd852636..86e465d416 100644 --- a/p2p/transport/websocket/addrs.go +++ b/p2p/transport/websocket/addrs.go @@ -1,3 +1,6 @@ +//go:build !js +// +build !js + package websocket import ( diff --git a/p2p/transport/websocket/addrs_test.go b/p2p/transport/websocket/addrs_test.go index 1a73c28762..2bed18e6c4 100644 --- a/p2p/transport/websocket/addrs_test.go +++ b/p2p/transport/websocket/addrs_test.go @@ -1,3 +1,6 @@ +//go:build !js +// +build !js + package websocket import ( diff --git a/p2p/transport/websocket/conn.go b/p2p/transport/websocket/conn.go index 0d4746086d..34ba375f3e 100644 --- a/p2p/transport/websocket/conn.go +++ b/p2p/transport/websocket/conn.go @@ -1,3 +1,6 @@ +//go:build !js +// +build !js + package websocket import ( diff --git a/p2p/transport/websocket/listener.go b/p2p/transport/websocket/listener.go index b7402c0e98..590b4205bc 100644 --- a/p2p/transport/websocket/listener.go +++ b/p2p/transport/websocket/listener.go @@ -1,3 +1,6 @@ +//go:build !js +// +build !js + package websocket import ( diff --git a/p2p/transport/websocket/websocket.go b/p2p/transport/websocket/websocket.go index 3a6badac5d..07c599377c 100644 --- a/p2p/transport/websocket/websocket.go +++ b/p2p/transport/websocket/websocket.go @@ -1,3 +1,6 @@ +//go:build !js +// +build !js + // Package websocket implements a websocket based transport for go-libp2p. package websocket diff --git a/p2p/transport/websocket/websocket_test.go b/p2p/transport/websocket/websocket_test.go index 2c47adf326..3d207b29ed 100644 --- a/p2p/transport/websocket/websocket_test.go +++ b/p2p/transport/websocket/websocket_test.go @@ -1,3 +1,6 @@ +//go:build !js +// +build !js + package websocket import ( diff --git a/p2p/transport/webtransport/cert_manager.go b/p2p/transport/webtransport/cert_manager.go index 494d5e7616..4a4be7a1aa 100644 --- a/p2p/transport/webtransport/cert_manager.go +++ b/p2p/transport/webtransport/cert_manager.go @@ -1,3 +1,6 @@ +//go:build !js +// +build !js + package libp2pwebtransport import ( diff --git a/p2p/transport/webtransport/cert_manager_test.go b/p2p/transport/webtransport/cert_manager_test.go index 942d47174e..1a6088ac81 100644 --- a/p2p/transport/webtransport/cert_manager_test.go +++ b/p2p/transport/webtransport/cert_manager_test.go @@ -1,3 +1,6 @@ +//go:build !js +// +build !js + package libp2pwebtransport import ( diff --git a/p2p/transport/webtransport/conn.go b/p2p/transport/webtransport/conn.go index f76ad10438..816c282505 100644 --- a/p2p/transport/webtransport/conn.go +++ b/p2p/transport/webtransport/conn.go @@ -1,3 +1,6 @@ +//go:build !js +// +build !js + package libp2pwebtransport import ( diff --git a/p2p/transport/webtransport/conn_js.go b/p2p/transport/webtransport/conn_js.go new file mode 100644 index 0000000000..746c9b3d58 --- /dev/null +++ b/p2p/transport/webtransport/conn_js.go @@ -0,0 +1,119 @@ +//go:build js + +package libp2pwebtransport + +import ( + "context" + "fmt" + "io" + "net" + "sync/atomic" + "syscall/js" + + ic "github.com/libp2p/go-libp2p/core/crypto" + "github.com/libp2p/go-libp2p/core/network" + "github.com/libp2p/go-libp2p/core/peer" + tpt "github.com/libp2p/go-libp2p/core/transport" + + ma "github.com/multiformats/go-multiaddr" +) + +var _ net.Addr = (*addr)(nil) + +type addr struct { + url string +} + +func (addr *addr) Network() string { + return "webtransport" +} + +func (addr *addr) String() string { + return addr.url +} + +type conn struct { + scope network.ConnScope + transport *transport + + wt js.Value + incoming js.Value + + rpid peer.ID + rpk ic.PubKey + + rmaddr ma.Multiaddr + raddr addr + + isClosed atomic.Bool + done bool +} + +func newConn(scope network.ConnScope, t *transport, wt js.Value, rmaddr ma.Multiaddr, p peer.ID, raddr addr) *conn { + return &conn{ + scope: scope, + transport: t, + wt: wt, + incoming: wt.Get("incomingBidirectionalStreams").Call("getReader"), + rpid: p, + rmaddr: rmaddr, + raddr: raddr, + } +} + +func (c *conn) OpenStream(ctx context.Context) (network.MuxedStream, error) { + return c.openStream(ctx) +} + +func (c *conn) openStream(ctx context.Context) (*stream, error) { + r, err := await(ctx, c.wt.Call("createBidirectionalStream")) + if err != nil { + return nil, fmt.Errorf("createBidirectionalStream: %w", err) + } + + return newStream(r[0], c), nil +} + +func (c *conn) AcceptStream() (network.MuxedStream, error) { + if c.done { + return nil, io.EOF + } + + r, err := await(context.Background(), c.incoming.Call("read")) + if err != nil { + return nil, err + } + o := r[0] + s := o.Get("value") + c.done = o.Get("done").Bool() + + return newStream(s, c), nil +} + +func (c *conn) Close() error { + c.wt.Call("close") + _, err := await(context.Background(), c.wt.Get("closed")) + c.isClosed.Store(true) + return err +} + +var noAddr = addr{"https://0.0.0.0" + webtransportHTTPEndpoint + "?type=noise"} + +func (c *conn) RemotePeer() peer.ID { return c.rpid } +func (c *conn) LocalPeer() peer.ID { return c.transport.pid } +func (c *conn) RemoteMultiaddr() ma.Multiaddr { return c.rmaddr } +func (c *conn) LocalMultiaddr() ma.Multiaddr { return webtransportMA } +func (c *conn) RemoteAddr() net.Addr { return &c.raddr } +func (c *conn) LocalAddr() net.Addr { return &noAddr } +func (c *conn) IsClosed() bool { return c.isClosed.Load() } +func (c *conn) Scope() network.ConnScope { return c.scope } +func (c *conn) Transport() tpt.Transport { return c.transport } +func (c *conn) RemotePublicKey() ic.PubKey { return c.rpk } + +func (c *conn) ConnState() network.ConnectionState { + return network.ConnectionState{Transport: "webtransport"} +} + +func (c *conn) CloseWithError(_ network.ConnErrorCode) error { + return c.Close() +} diff --git a/p2p/transport/webtransport/crypto.go b/p2p/transport/webtransport/crypto.go index 90504ead01..258557d4d4 100644 --- a/p2p/transport/webtransport/crypto.go +++ b/p2p/transport/webtransport/crypto.go @@ -1,3 +1,6 @@ +//go:build !js +// +build !js + package libp2pwebtransport import ( diff --git a/p2p/transport/webtransport/crypto_test.go b/p2p/transport/webtransport/crypto_test.go index ba439c28af..0eddd95e47 100644 --- a/p2p/transport/webtransport/crypto_test.go +++ b/p2p/transport/webtransport/crypto_test.go @@ -1,3 +1,6 @@ +//go:build !js +// +build !js + package libp2pwebtransport import ( diff --git a/p2p/transport/webtransport/listener.go b/p2p/transport/webtransport/listener.go index ff611fe927..0920f706eb 100644 --- a/p2p/transport/webtransport/listener.go +++ b/p2p/transport/webtransport/listener.go @@ -1,3 +1,6 @@ +//go:build !js +// +build !js + package libp2pwebtransport import ( diff --git a/p2p/transport/webtransport/multiaddr_test.go b/p2p/transport/webtransport/multiaddr_test.go index 3f0a3ec0bf..2213902374 100644 --- a/p2p/transport/webtransport/multiaddr_test.go +++ b/p2p/transport/webtransport/multiaddr_test.go @@ -1,3 +1,6 @@ +//go:build !js +// +build !js + package libp2pwebtransport import ( diff --git a/p2p/transport/webtransport/stream.go b/p2p/transport/webtransport/stream.go index 115d3a8344..8f8abfce1f 100644 --- a/p2p/transport/webtransport/stream.go +++ b/p2p/transport/webtransport/stream.go @@ -1,3 +1,6 @@ +//go:build !js +// +build !js + package libp2pwebtransport import ( @@ -72,7 +75,7 @@ func (s *stream) Reset() error { // ResetWithError resets the stream ignoring the error code. Error codes aren't // specified for WebTransport as the current implementation of WebTransport in -// browsers(https://www.ietf.org/archive/id/draft-kinnear-webtransport-http2-02.html) +// browsers (https://www.ietf.org/archive/id/draft-kinnear-webtransport-http2-02.html) // only supports 1 byte error codes. For more details, see // https://github.com/libp2p/specs/blob/4eca305185c7aef219e936bef76c48b1ab0a8b43/error-codes/README.md?plain=1#L84 func (s *stream) ResetWithError(_ network.StreamErrorCode) error { diff --git a/p2p/transport/webtransport/stream_js.go b/p2p/transport/webtransport/stream_js.go new file mode 100644 index 0000000000..682c347d22 --- /dev/null +++ b/p2p/transport/webtransport/stream_js.go @@ -0,0 +1,174 @@ +//go:build js + +package libp2pwebtransport + +import ( + "context" + "io" + "net" + "sync" + "sync/atomic" + "syscall/js" + "time" + + "github.com/libp2p/go-libp2p/core/network" + "go.uber.org/multierr" +) + +type stream struct { + conn *conn + read, write js.Value + readMux sync.Mutex + readBuf js.Value + done bool + + deadline, readDeadline, writeDeadline atomic.Pointer[time.Time] +} + +func newStream(s js.Value, c *conn) *stream { + return &stream{ + read: s.Get("readable").Call("getReader"), + write: s.Get("writable").Call("getWriter"), + readBuf: js.Global().Get("Uint8Array").New(0), + conn: c, + } +} + +func (s *stream) Read(b []byte) (n int, err error) { + if len(b) == 0 { + // We use a zero copy as detection of an empty array bellow, so we need a + // a non empty destination. + return + } + + s.readMux.Lock() + defer s.readMux.Unlock() + if s.done { + return 0, io.EOF + } + n = js.CopyBytesToGo(b, s.readBuf) + if n != 0 { + s.readBuf = s.readBuf.Call("slice", n) + return + } + + ctx := context.Background() + var deadline, readDeadline time.Time + if t := s.deadline.Load(); t != nil { + deadline = *t + } + if t := s.readDeadline.Load(); t != nil { + readDeadline = *t + } + switch { + case deadline.IsZero() && !readDeadline.IsZero(): + var cancel context.CancelFunc + ctx, cancel = context.WithDeadline(ctx, readDeadline) + defer cancel() + case !deadline.IsZero(): + max := deadline + if deadline.Before(readDeadline) { + max = readDeadline + } + var cancel context.CancelFunc + ctx, cancel = context.WithDeadline(ctx, max) + defer cancel() + } + + r, err := await(ctx, s.read.Call("read")) + if err != nil { + return 0, err + } + o := r[0] + s.readBuf = o.Get("value") + s.done = o.Get("done").Bool() + + if s.done && s.readBuf.IsUndefined() { + return 0, io.EOF + } + + n = js.CopyBytesToGo(b, s.readBuf) + s.readBuf = s.readBuf.Call("slice", n) + return +} + +func (s *stream) Write(b []byte) (n int, err error) { + if len(b) == 0 { + return + } + + ctx := context.Background() + var deadline, writeDeadline time.Time + if t := s.deadline.Load(); t != nil { + deadline = *t + } + if t := s.writeDeadline.Load(); t != nil { + writeDeadline = *t + } + switch { + case deadline.IsZero() && !writeDeadline.IsZero(): + var cancel context.CancelFunc + ctx, cancel = context.WithDeadline(ctx, writeDeadline) + defer cancel() + case !deadline.IsZero(): + max := deadline + if deadline.Before(writeDeadline) { + max = writeDeadline + } + var cancel context.CancelFunc + ctx, cancel = context.WithDeadline(ctx, max) + defer cancel() + } + + _, err = await(ctx, s.write.Call("write", byteSliceToJS(b))) + if err != nil { + return 0, err + } + + return len(b), nil +} + +func (s *stream) Reset() error { + return multierr.Combine(s.resetWrite(), s.CloseRead()) +} + +func (s *stream) ResetWithError(_ network.StreamErrorCode) error { + return s.Reset() +} + +func (s *stream) Close() error { + return multierr.Combine(s.CloseWrite(), s.CloseRead()) +} + +func (s *stream) CloseWrite() error { + _, err := await(context.Background(), s.write.Call("close")) + return err +} + +func (s *stream) resetWrite() error { + _, err := await(context.Background(), s.write.Call("abort")) + return err +} + +func (s *stream) CloseRead() error { + _, err := await(context.Background(), s.read.Call("cancel")) + return err +} + +func (s *stream) RemoteAddr() net.Addr { return s.conn.RemoteAddr() } +func (s *stream) LocalAddr() net.Addr { return s.conn.LocalAddr() } + +func (s *stream) SetDeadline(t time.Time) error { + s.deadline.Store(&t) + return nil +} + +func (s *stream) SetReadDeadline(t time.Time) error { + s.readDeadline.Store(&t) + return nil +} + +func (s *stream) SetWriteDeadline(t time.Time) error { + s.writeDeadline.Store(&t) + return nil +} diff --git a/p2p/transport/webtransport/transport.go b/p2p/transport/webtransport/transport.go index 8818c7b8a7..d8aa9e68f1 100644 --- a/p2p/transport/webtransport/transport.go +++ b/p2p/transport/webtransport/transport.go @@ -1,3 +1,6 @@ +//go:build !js +// +build !js + package libp2pwebtransport import ( @@ -23,7 +26,6 @@ import ( "github.com/libp2p/go-libp2p/p2p/transport/quicreuse" "github.com/benbjohnson/clock" - logging "github.com/ipfs/go-log/v2" ma "github.com/multiformats/go-multiaddr" manet "github.com/multiformats/go-multiaddr/net" "github.com/multiformats/go-multihash" @@ -32,14 +34,6 @@ import ( "github.com/quic-go/webtransport-go" ) -var log = logging.Logger("webtransport") - -const webtransportHTTPEndpoint = "/.well-known/libp2p-webtransport" - -const errorCodeConnectionGating = 0x47415445 // GATE in ASCII - -const certValidity = 14 * 24 * time.Hour - type Option func(*transport) error func WithClock(cl clock.Clock) Option { @@ -68,13 +62,10 @@ func WithHandshakeTimeout(d time.Duration) Option { } type transport struct { - privKey ic.PrivKey - pid peer.ID - clock clock.Clock + common + clock clock.Clock connManager *quicreuse.ConnManager - rcmgr network.ResourceManager - gater connmgr.ConnectionGater listenOnce sync.Once listenOnceErr error @@ -83,8 +74,6 @@ type transport struct { staticTLSConf *tls.Config tlsClientConf *tls.Config - noise *noise.Transport - connMx sync.Mutex conns map[quic.ConnectionTracingID]*conn // using quic-go's ConnectionTracingKey as map key handshakeTimeout time.Duration @@ -107,10 +96,12 @@ func New(key ic.PrivKey, psk pnet.PSK, connManager *quicreuse.ConnManager, gater return nil, err } t := &transport{ - pid: id, - privKey: key, - rcmgr: rcmgr, - gater: gater, + common: common{ + privKey: key, + pid: id, + rcmgr: rcmgr, + gater: gater, + }, clock: clock.New(), connManager: connManager, conns: map[quic.ConnectionTracingID]*conn{}, @@ -290,18 +281,6 @@ func (t *transport) upgrade(ctx context.Context, sess *webtransport.Session, p p }, nil } -func decodeCertHashesFromProtobuf(b [][]byte) ([]multihash.DecodedMultihash, error) { - hashes := make([]multihash.DecodedMultihash, 0, len(b)) - for _, h := range b { - dh, err := multihash.Decode(h) - if err != nil { - return nil, fmt.Errorf("failed to decode hash: %w", err) - } - hashes = append(hashes, *dh) - } - return hashes, nil -} - func (t *transport) CanDial(addr ma.Multiaddr) bool { ok, _ := IsWebtransportMultiaddr(addr) return ok @@ -380,28 +359,6 @@ func (t *transport) removeConn(sess *webtransport.Session) { t.connMx.Unlock() } -// extractSNI returns what the SNI should be for the given maddr. If there is an -// SNI component in the multiaddr, then it will be returned and -// foundSniComponent will be true. If there's no SNI component, but there is a -// DNS-like component, then that will be returned for the sni and -// foundSniComponent will be false (since we didn't find an actual sni component). -func extractSNI(maddr ma.Multiaddr) (sni string, foundSniComponent bool) { - ma.ForEach(maddr, func(c ma.Component) bool { - switch c.Protocol().Code { - case ma.P_SNI: - sni = c.Value() - foundSniComponent = true - return false - case ma.P_DNS, ma.P_DNS4, ma.P_DNS6, ma.P_DNSADDR: - sni = c.Value() - // Keep going in case we find an `sni` component - return true - } - return true - }) - return sni, foundSniComponent -} - // Resolve implements transport.Resolver func (t *transport) Resolve(_ context.Context, maddr ma.Multiaddr) ([]ma.Multiaddr, error) { sni, foundSniComponent := extractSNI(maddr) diff --git a/p2p/transport/webtransport/transport_js.go b/p2p/transport/webtransport/transport_js.go new file mode 100644 index 0000000000..bbd0308b0b --- /dev/null +++ b/p2p/transport/webtransport/transport_js.go @@ -0,0 +1,282 @@ +//go:build js + +package libp2pwebtransport + +import ( + "bytes" + "context" + "errors" + "fmt" + "net" + "syscall/js" + + "github.com/libp2p/go-libp2p/core/connmgr" + ic "github.com/libp2p/go-libp2p/core/crypto" + "github.com/libp2p/go-libp2p/core/network" + "github.com/libp2p/go-libp2p/core/peer" + "github.com/libp2p/go-libp2p/core/pnet" + "github.com/libp2p/go-libp2p/core/sec" + tpt "github.com/libp2p/go-libp2p/core/transport" + "github.com/libp2p/go-libp2p/p2p/security/noise" + "github.com/libp2p/go-libp2p/p2p/security/noise/pb" + + ma "github.com/multiformats/go-multiaddr" + manet "github.com/multiformats/go-multiaddr/net" + "github.com/multiformats/go-multihash" + "go.uber.org/multierr" +) + +type transport struct { + common +} + +type Option func(*transport) error + +func (t *transport) verifyChallengeOnOutboundConnection(ctx context.Context, conn net.Conn, p peer.ID, certHashes []multihash.DecodedMultihash) (sec.SecureConn, error) { + // Now run a Noise handshake (using early data) and get all the certificate hashes from the server. + // We will verify that the certhashes we used to dial is a subset of the certhashes we received from the server. + var verified bool + n, err := t.noise.WithSessionOptions(noise.EarlyData(newEarlyDataReceiver(func(b *pb.NoiseExtensions) error { + decodedCertHashes, err := decodeCertHashesFromProtobuf(b.WebtransportCerthashes) + if err != nil { + return err + } + for _, sent := range certHashes { + var found bool + for _, rcvd := range decodedCertHashes { + if sent.Code == rcvd.Code && bytes.Equal(sent.Digest, rcvd.Digest) { + found = true + break + } + } + if !found { + return fmt.Errorf("missing cert hash: %v", sent) + } + } + verified = true + return nil + }), nil)) + if err != nil { + return nil, fmt.Errorf("failed to create Noise transport: %w", err) + } + c, err := n.SecureOutbound(ctx, conn, p) + if err != nil { + return nil, err + } + if err = c.Close(); err != nil { + return nil, err + } + // The Noise handshake _should_ guarantee that our verification callback is called. + // Double-check just in case. + if !verified { + return nil, errors.New("didn't verify") + } + + return c, nil +} + +func New(key ic.PrivKey, psk pnet.PSK, gater connmgr.ConnectionGater, rcmgr network.ResourceManager, opts ...Option) (tpt.Transport, error) { + if len(psk) > 0 { + return nil, errors.New("WebTransport doesn't support private networks yet") + } + if rcmgr == nil { + rcmgr = &network.NullResourceManager{} + } + pid, err := peer.IDFromPrivateKey(key) + if err != nil { + return nil, err + } + + noiseTransport, err := noise.New(noise.ID, key, nil) + if err != nil { + return nil, fmt.Errorf("failed to initialize noise transport: %w", err) + } + + t := &transport{ + common: common{ + privKey: key, + pid: pid, + rcmgr: rcmgr, + gater: gater, + noise: noiseTransport, + }, + } + + // Apply any additional options + for _, opt := range opts { + if err := opt(t); err != nil { + return nil, err + } + } + + return t, nil +} + +func (t *transport) Protocols() []int { + return []int{ma.P_WEBTRANSPORT} +} + +func (t *transport) Proxy() bool { + return false +} + +func (t *transport) Listen(laddr ma.Multiaddr) (tpt.Listener, error) { + return nil, errors.New("Listen is not supported in WASM for WebTransport") +} + +func (t *transport) CanDial(addr ma.Multiaddr) bool { + ok, _ := IsWebtransportMultiaddr(addr) + return ok +} + +// await tries to await a piece of code, it will leave the promise in an undefined +// state if the context is canceled or expires. +func await(ctx context.Context, v js.Value) (success []js.Value, err error) { + // This does not look very efficient but I don't care about performance right + // now and this makes the code WAY more readable than callback hell. + c := make(chan struct{}, 1) + var s, f js.Func + s = js.FuncOf(func(_ js.Value, args []js.Value) any { + success = args + c <- struct{}{} + s.Release() + f.Release() + return nil + }) + f = js.FuncOf(func(_ js.Value, args []js.Value) any { + errs := make([]error, len(args)) + for i, v := range args { + errs[i] = errors.New(v.String()) + } + err = fmt.Errorf("JS catch: %w", multierr.Combine(errs...)) + c <- struct{}{} + s.Release() + f.Release() + return nil + }) + + // Here we create an adhoc promise that we will race against the real one. + // This allows us to callback into s which will Release s and f. Removing + // references to v and hopefully allowing the JS GC to cancel the promise. + var resolve js.Value + capture := js.FuncOf(func(_ js.Value, args []js.Value) any { + resolve = args[0] + return nil + }) + promises := js.Global().Get("Promise") + stopper := promises.New(capture) + capture.Release() + promises.Call("race", []any{v, stopper}).Call("then", s, f) + select { + case <-ctx.Done(): + resolve.Invoke() // This will trigger s and cleanup in a thread safe manner. + return nil, ctx.Err() + case <-c: + return + } +} + +func byteSliceToJS(buf []byte) js.Value { + uint8Array := js.Global().Get("Uint8Array").New(len(buf)) + if js.CopyBytesToJS(uint8Array, buf) != len(buf) { + panic("expected to copy all bytes") + } + return uint8Array +} + +func (t *transport) Dial(ctx context.Context, raddr ma.Multiaddr, p peer.ID) (tpt.CapableConn, error) { + // Open a connection scope with the resource manager + scope, err := t.rcmgr.OpenConnection(network.DirOutbound, false, raddr) + if err != nil { + log.Debugw("resource manager blocked outgoing connection", "peer", p, "addr", raddr, "error", err) + return nil, err + } + + // Call the dialWithScope method to handle the actual dialing process + c, err := t.dialWithScope(ctx, raddr, p, scope) + if err != nil { + scope.Done() + return nil, err + } + + return c, nil +} + +func (t *transport) dialWithScope(ctx context.Context, raddr ma.Multiaddr, p peer.ID, scope network.ConnManagementScope) (tpt.CapableConn, error) { + certHashes, err := extractCertHashes(raddr) + if err != nil { + return nil, err + } + + if len(certHashes) == 0 { + return nil, errors.New("can't dial webtransport without certhashes") + } + + sni, _ := extractSNI(raddr) + + if err := scope.SetPeer(p); err != nil { + log.Debugw("resource manager blocked outgoing connection for peer", "peer", p, "addr", raddr, "error", err) + return nil, err + } + + maddr, _ := ma.SplitFunc(raddr, func(c ma.Component) bool { return c.Protocol().Code == ma.P_WEBTRANSPORT }) + return t.dial(ctx, maddr, p, sni, certHashes, scope) +} + +func (t *transport) dial(ctx context.Context, tgt ma.Multiaddr, p peer.ID, sni string, certHashes []multihash.DecodedMultihash, scope network.ConnManagementScope) (tpt.CapableConn, error) { + webtransport := js.Global().Get("WebTransport") + if webtransport.IsUndefined() { + return nil, fmt.Errorf("WebTransport is not supported in your browser") + } + + var raddr string + if sni != "" { + raddr = sni + } else { + var err error + _, raddr, err = manet.DialArgs(tgt) + if err != nil { + return nil, err + } + } + + url := fmt.Sprintf("https://%s%s?type=noise", raddr, webtransportHTTPEndpoint) + + ch := make([]any, len(certHashes)) + for i, h := range certHashes { + if h.Code != multihash.SHA2_256 { + // https://developer.mozilla.org/en-US/docs/Web/API/WebTransport/WebTransport#parameters + // At time of writing, SHA-256 is the only hash algorithm listed in the specification. + continue + } + + ch[i] = map[string]any{ + "algorithm": "sha-256", + "value": byteSliceToJS(h.Digest), + } + } + + wt := webtransport.New(url, map[string]any{"serverCertificateHashes": ch}) + _, err := await(ctx, wt.Get("ready")) + if err != nil { + return nil, fmt.Errorf("initial connection: %w", err) + } + + c := newConn(scope, t, wt, tgt, p, addr{url}) + + s, err := c.openStream(ctx) + if err != nil { + c.Close() + return nil, err + } + defer s.Close() + + verified, err := t.verifyChallengeOnOutboundConnection(ctx, s, p, certHashes) + if err != nil { + c.Close() + return nil, fmt.Errorf("verifying challenge: %w", err) + } + c.rpk = verified.RemotePublicKey() + + return c, nil +} diff --git a/p2p/transport/webtransport/transport_shared.go b/p2p/transport/webtransport/transport_shared.go new file mode 100644 index 0000000000..787a71df0f --- /dev/null +++ b/p2p/transport/webtransport/transport_shared.go @@ -0,0 +1,66 @@ +package libp2pwebtransport + +import ( + "fmt" + "time" + + logging "github.com/ipfs/go-log/v2" + "github.com/libp2p/go-libp2p/core/connmgr" + ic "github.com/libp2p/go-libp2p/core/crypto" + "github.com/libp2p/go-libp2p/core/network" + "github.com/libp2p/go-libp2p/core/peer" + "github.com/libp2p/go-libp2p/p2p/security/noise" + ma "github.com/multiformats/go-multiaddr" + "github.com/multiformats/go-multihash" +) + +var log = logging.Logger("webtransport") + +const webtransportHTTPEndpoint = "/.well-known/libp2p-webtransport" + +const errorCodeConnectionGating = 0x47415445 // GATE in ASCII + +const certValidity = 14 * 24 * time.Hour + +type common struct { + privKey ic.PrivKey + pid peer.ID + + noise *noise.Transport + gater connmgr.ConnectionGater + rcmgr network.ResourceManager +} + +// extractSNI returns what the SNI should be for the given maddr. If there is an +// SNI component in the multiaddr, then it will be returned and +// foundSniComponent will be true. If there's no SNI component, but there is a +// DNS-like component, then that will be returned for the sni and +// foundSniComponent will be false (since we didn't find an actual sni component). +func extractSNI(maddr ma.Multiaddr) (sni string, foundSniComponent bool) { + ma.ForEach(maddr, func(c ma.Component) bool { + switch c.Protocol().Code { + case ma.P_SNI: + sni = c.Value() + foundSniComponent = true + return false + case ma.P_DNS, ma.P_DNS4, ma.P_DNS6, ma.P_DNSADDR: + sni = c.Value() + // Keep going in case we find an `sni` component + return true + } + return true + }) + return sni, foundSniComponent +} + +func decodeCertHashesFromProtobuf(b [][]byte) ([]multihash.DecodedMultihash, error) { + hashes := make([]multihash.DecodedMultihash, 0, len(b)) + for _, h := range b { + dh, err := multihash.Decode(h) + if err != nil { + return nil, fmt.Errorf("failed to decode hash: %w", err) + } + hashes = append(hashes, *dh) + } + return hashes, nil +} diff --git a/p2p/transport/webtransport/transport_test.go b/p2p/transport/webtransport/transport_test.go index 38968b110d..88edf1f156 100644 --- a/p2p/transport/webtransport/transport_test.go +++ b/p2p/transport/webtransport/transport_test.go @@ -1,3 +1,6 @@ +//go:build !js +// +build !js + package libp2pwebtransport_test import ( diff --git a/test-plans/cmd/ping/main.go b/test-plans/cmd/ping/main.go index c836a72e72..8e49653fe7 100644 --- a/test-plans/cmd/ping/main.go +++ b/test-plans/cmd/ping/main.go @@ -1,3 +1,6 @@ +//go:build !js +// +build !js + package main import (