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/libp2p/go-libp2p/p2p/transport/quicreuse" "github.com/prometheus/client_golang/prometheus" ma "github.com/multiformats/go-multiaddr" madns "github.com/multiformats/go-multiaddr-dns" "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"` typ := reflect.ValueOf(constructor).Type() numParams := typ.NumIn() isVariadic := typ.IsVariadic() if !isVariadic && len(opts) > 0 { return errors.New("QUICReuse constructor doesn't take any options") } var params []string if isVariadic && len(opts) > 0 { // If there are 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.QUICReuse = append(cfg.QUICReuse, fx.Provide(fx.Annotate(constructor, fx.ParamTags(params...)))) for _, opt := range opts { cfg.QUICReuse = append(cfg.QUICReuse, fx.Supply(fx.Annotate(opt, fx.ResultTags(tag)))) } 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 *madns.Resolver) 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 } }