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 (