 9bdcbe0447
			
		
	
	9bdcbe0447
	
	
	
		
			
			Major integrations and fixes: - Added BACKBEAT SDK integration for P2P operation timing - Implemented beat-aware status tracking for distributed operations - Added Docker secrets support for secure license management - Resolved KACHING license validation via HTTPS/TLS - Updated docker-compose configuration for clean stack deployment - Disabled rollback policies to prevent deployment failures - Added license credential storage (CHORUS-DEV-MULTI-001) Technical improvements: - BACKBEAT P2P operation tracking with phase management - Enhanced configuration system with file-based secrets - Improved error handling for license validation - Clean separation of KACHING and CHORUS deployment stacks 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
		
			
				
	
	
		
			499 lines
		
	
	
		
			15 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			499 lines
		
	
	
		
			15 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
| package config
 | |
| 
 | |
| import (
 | |
| 	"crypto/rand"
 | |
| 	"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/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"
 | |
| 	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/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"
 | |
| 	"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
 | |
| // 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
 | |
| 
 | |
| 	QUICReuse          []fx.Option
 | |
| 	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 *madns.Resolver
 | |
| 
 | |
| 	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
 | |
| }
 | |
| 
 | |
| 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 := cfg.SwarmOpts
 | |
| 	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) addTransports(h host.Host) error {
 | |
| 	swrm, ok := h.Network().(transport.TransportNetwork)
 | |
| 	if !ok {
 | |
| 		// Should probably skip this if no transports.
 | |
| 		return fmt.Errorf("swarm does not support transports")
 | |
| 	}
 | |
| 
 | |
| 	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.Supply(h.ID()),
 | |
| 		fx.Provide(func() host.Host { return h }),
 | |
| 		fx.Provide(func() crypto.PrivKey { return h.Peerstore().PrivKey(h.ID()) }),
 | |
| 		fx.Provide(func() connmgr.ConnectionGater { return cfg.ConnectionGater }),
 | |
| 		fx.Provide(func() pnet.PSK { return cfg.PSK }),
 | |
| 		fx.Provide(func() network.ResourceManager { return cfg.ResourceManager }),
 | |
| 		fx.Provide(func() *madns.Resolver { return cfg.MultiaddrResolver }),
 | |
| 	}
 | |
| 	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))
 | |
| 	if cfg.QUICReuse != nil {
 | |
| 		fxopts = append(fxopts, cfg.QUICReuse...)
 | |
| 	} else {
 | |
| 		fxopts = append(fxopts, fx.Provide(quicreuse.NewConnManager)) // TODO: close the ConnManager when shutting down the node
 | |
| 	}
 | |
| 
 | |
| 	fxopts = append(fxopts, fx.Invoke(
 | |
| 		fx.Annotate(
 | |
| 			func(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))
 | |
| 	}
 | |
| 	app := fx.New(fxopts...)
 | |
| 	if err := app.Err(); err != nil {
 | |
| 		h.Close()
 | |
| 		return err
 | |
| 	}
 | |
| 	return 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) {
 | |
| 	// 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))
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	eventBus := eventbus.NewBus(eventbus.WithMetricsTracer(eventbus.NewMetricsTracer(eventbus.WithRegisterer(cfg.PrometheusRegisterer))))
 | |
| 	swrm, err := cfg.makeSwarm(eventBus, !cfg.DisableMetrics)
 | |
| 	if err != nil {
 | |
| 		return nil, err
 | |
| 	}
 | |
| 
 | |
| 	if !cfg.DisableMetrics {
 | |
| 		rcmgr.MustRegisterWith(cfg.PrometheusRegisterer)
 | |
| 	}
 | |
| 
 | |
| 	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,
 | |
| 	})
 | |
| 	if err != nil {
 | |
| 		swrm.Close()
 | |
| 		return nil, err
 | |
| 	}
 | |
| 
 | |
| 	if cfg.Relay {
 | |
| 		// If we've enabled the relay, we should filter out relay
 | |
| 		// addresses by default.
 | |
| 		//
 | |
| 		// TODO: We shouldn't be doing this here.
 | |
| 		oldFactory := h.AddrsFactory
 | |
| 		h.AddrsFactory = func(addrs []ma.Multiaddr) []ma.Multiaddr {
 | |
| 			return oldFactory(autorelay.Filter(addrs))
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	if err := cfg.addTransports(h); err != nil {
 | |
| 		h.Close()
 | |
| 		return nil, err
 | |
| 	}
 | |
| 
 | |
| 	// TODO: This method succeeds if listening on one address succeeds. We
 | |
| 	// should probably fail if listening on *any* addr fails.
 | |
| 	if err := h.Network().Listen(cfg.ListenAddrs...); err != nil {
 | |
| 		h.Close()
 | |
| 		return nil, err
 | |
| 	}
 | |
| 
 | |
| 	// Configure routing and autorelay
 | |
| 	var router routing.PeerRouting
 | |
| 	if cfg.Routing != nil {
 | |
| 		router, err = cfg.Routing(h)
 | |
| 		if err != nil {
 | |
| 			h.Close()
 | |
| 			return nil, err
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	// Note: h.AddrsFactory may be changed by relayFinder, but non-relay version is
 | |
| 	// used by AutoNAT below.
 | |
| 	var ar *autorelay.AutoRelay
 | |
| 	addrF := h.AddrsFactory
 | |
| 	if cfg.EnableAutoRelay {
 | |
| 		if !cfg.Relay {
 | |
| 			h.Close()
 | |
| 			return nil, fmt.Errorf("cannot enable autorelay; relay is not enabled")
 | |
| 		}
 | |
| 		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 nil, err
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	autonatOpts := []autonat.Option{
 | |
| 		autonat.UsingAddresses(func() []ma.Multiaddr {
 | |
| 			return addrF(h.AllAddrs())
 | |
| 		}),
 | |
| 	}
 | |
| 	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 nil, err
 | |
| 		}
 | |
| 		ps, err := pstoremem.NewPeerstore()
 | |
| 		if err != nil {
 | |
| 			return nil, err
 | |
| 		}
 | |
| 
 | |
| 		// Pull out the pieces of the config that we _actually_ care about.
 | |
| 		// Specifically, don't set up things like autorelay, 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,
 | |
| 			SwarmOpts: []swarm.Option{
 | |
| 				// It is better to disable black hole detection and just attempt a dial for autonat
 | |
| 				swarm.WithUDPBlackHoleConfig(false, 0, 0),
 | |
| 				swarm.WithIPv6BlackHoleConfig(false, 0, 0),
 | |
| 			},
 | |
| 		}
 | |
| 
 | |
| 		dialer, err := autoNatCfg.makeSwarm(eventbus.NewBus(), false)
 | |
| 		if err != nil {
 | |
| 			h.Close()
 | |
| 			return nil, err
 | |
| 		}
 | |
| 		dialerHost := blankhost.NewBlankHost(dialer)
 | |
| 		if err := autoNatCfg.addTransports(dialerHost); err != nil {
 | |
| 			dialerHost.Close()
 | |
| 			h.Close()
 | |
| 			return nil, err
 | |
| 		}
 | |
| 		// NOTE: We're dropping the blank host here but that's fine. It
 | |
| 		// doesn't really _do_ anything and doesn't even need to be
 | |
| 		// closed (as long as we close the underlying network).
 | |
| 		autonatOpts = append(autonatOpts, autonat.EnableService(dialerHost.Network()))
 | |
| 	}
 | |
| 	if cfg.AutoNATConfig.ForceReachability != nil {
 | |
| 		autonatOpts = append(autonatOpts, autonat.WithReachability(*cfg.AutoNATConfig.ForceReachability))
 | |
| 	}
 | |
| 
 | |
| 	autonat, err := autonat.New(h, autonatOpts...)
 | |
| 	if err != nil {
 | |
| 		h.Close()
 | |
| 		return nil, fmt.Errorf("cannot enable autorelay; autonat failed to start: %v", err)
 | |
| 	}
 | |
| 	h.SetAutoNat(autonat)
 | |
| 
 | |
| 	// start the host background tasks
 | |
| 	h.Start()
 | |
| 
 | |
| 	var ho host.Host
 | |
| 	ho = h
 | |
| 	if router != nil {
 | |
| 		ho = routed.Wrap(h, router)
 | |
| 	}
 | |
| 	if ar != nil {
 | |
| 		arh := autorelay.NewAutoRelayHost(ho, ar)
 | |
| 		arh.Start()
 | |
| 		ho = arh
 | |
| 	}
 | |
| 	return ho, 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
 | |
| }
 |