core.go 15.5 KB
Newer Older
1 2 3 4
/*
Package core implements the IpfsNode object and related methods.

Packages underneath core/ provide a (relatively) stable, low-level API
5 6 7 8
to carry out most IPFS-related tasks.  For more details on the other
interfaces and how core/... fits into the bigger IPFS picture, see:

  $ godoc github.com/ipfs/go-ipfs
9
*/
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
10 11
package core

Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
12
import (
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
13
	"errors"
Juan Batiz-Benet's avatar
go fmt  
Juan Batiz-Benet committed
14
	"fmt"
15
	"io"
Jeromy's avatar
Jeromy committed
16
	"net"
17
	"time"
18

19 20 21
	b58 "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-base58"
	ds "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-datastore"
	ma "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multiaddr"
22
	goprocess "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/goprocess"
Jeromy's avatar
Jeromy committed
23
	mamask "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/whyrusleeping/multiaddr-filter"
24 25
	context "github.com/ipfs/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context"
	diag "github.com/ipfs/go-ipfs/diagnostics"
Jeromy's avatar
Jeromy committed
26
	metrics "github.com/ipfs/go-ipfs/metrics"
27
	ic "github.com/ipfs/go-ipfs/p2p/crypto"
28
	discovery "github.com/ipfs/go-ipfs/p2p/discovery"
29 30 31 32 33 34
	p2phost "github.com/ipfs/go-ipfs/p2p/host"
	p2pbhost "github.com/ipfs/go-ipfs/p2p/host/basic"
	rhost "github.com/ipfs/go-ipfs/p2p/host/routed"
	swarm "github.com/ipfs/go-ipfs/p2p/net/swarm"
	addrutil "github.com/ipfs/go-ipfs/p2p/net/swarm/addr"
	peer "github.com/ipfs/go-ipfs/p2p/peer"
Jeromy's avatar
Jeromy committed
35
	ping "github.com/ipfs/go-ipfs/p2p/protocol/ping"
Jeromy's avatar
Jeromy committed
36
	logging "github.com/ipfs/go-ipfs/vendor/QmQg1J6vikuXF9oDvm4wpdeAUvvkVEKW1EYDw9HhTMnP2b/go-log"
37

38 39
	routing "github.com/ipfs/go-ipfs/routing"
	dht "github.com/ipfs/go-ipfs/routing/dht"
40
	nilrouting "github.com/ipfs/go-ipfs/routing/none"
41
	offroute "github.com/ipfs/go-ipfs/routing/offline"
42

43 44 45 46 47 48
	bstore "github.com/ipfs/go-ipfs/blocks/blockstore"
	bserv "github.com/ipfs/go-ipfs/blockservice"
	exchange "github.com/ipfs/go-ipfs/exchange"
	bitswap "github.com/ipfs/go-ipfs/exchange/bitswap"
	bsnet "github.com/ipfs/go-ipfs/exchange/bitswap/network"
	rp "github.com/ipfs/go-ipfs/exchange/reprovide"
49

50 51 52 53
	mount "github.com/ipfs/go-ipfs/fuse/mount"
	ipnsfs "github.com/ipfs/go-ipfs/ipnsfs"
	merkledag "github.com/ipfs/go-ipfs/merkledag"
	namesys "github.com/ipfs/go-ipfs/namesys"
Jeromy's avatar
Jeromy committed
54
	ipnsrp "github.com/ipfs/go-ipfs/namesys/republisher"
55 56 57 58
	path "github.com/ipfs/go-ipfs/path"
	pin "github.com/ipfs/go-ipfs/pin"
	repo "github.com/ipfs/go-ipfs/repo"
	config "github.com/ipfs/go-ipfs/repo/config"
59
	u "github.com/ipfs/go-ipfs/util"
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
60 61
)

Jeromy's avatar
Jeromy committed
62
const IpnsValidatorTag = "ipns"
63
const kSizeBlockstoreWriteCache = 100
64
const kReprovideFrequency = time.Hour * 12
65
const discoveryConnTimeout = time.Second * 30
Jeromy's avatar
Jeromy committed
66

Jeromy's avatar
Jeromy committed
67
var log = logging.Logger("core")
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
68

69 70 71 72 73 74 75 76 77
type mode int

const (
	// zero value is not a valid mode, must be explicitly set
	invalidMode mode = iota
	offlineMode
	onlineMode
)

Juan Batiz-Benet's avatar
go lint  
Juan Batiz-Benet committed
78
// IpfsNode is IPFS Core module. It represents an IPFS instance.
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
79 80
type IpfsNode struct {

81
	// Self
82
	Identity peer.ID // the local node's identity
83

84
	Repo repo.Repo
85 86

	// Local node
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
87 88 89
	Pinning    pin.Pinner // the pinning manager
	Mounts     Mounts     // current mount state, if any.
	PrivateKey ic.PrivKey // the local node's private Key
90 91

	// Services
92 93 94 95 96
	Peerstore  peer.Peerstore       // storage for other Peer instances
	Blockstore bstore.Blockstore    // the block store (lower level)
	Blocks     *bserv.BlockService  // the block service, get/add blocks.
	DAG        merkledag.DAGService // the merkle dag service, get/add objects.
	Resolver   *path.Resolver       // the path resolution system
Jeromy's avatar
Jeromy committed
97
	Reporter   metrics.Reporter
98
	Discovery  discovery.Service
99 100

	// Online
101 102 103 104 105 106
	PeerHost     p2phost.Host        // the network host (server+client)
	Bootstrapper io.Closer           // the periodic bootstrapper
	Routing      routing.IpfsRouting // the routing system. recommend ipfs-dht
	Exchange     exchange.Interface  // the block exchange + strategy (bitswap)
	Namesys      namesys.NameSystem  // the name system, resolves paths to hashes
	Diagnostics  *diag.Diagnostics   // the diagnostics service
Jeromy's avatar
Jeromy committed
107 108
	Ping         *ping.PingService
	Reprovider   *rp.Reprovider // the value reprovider system
Jeromy's avatar
Jeromy committed
109
	IpnsRepub    *ipnsrp.Republisher
110

111 112
	IpnsFs *ipnsfs.Filesystem

113 114
	proc goprocess.Process
	ctx  context.Context
115

116
	mode mode
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
117 118
}

119 120 121 122 123 124 125 126
// Mounts defines what the node's mount state is. This should
// perhaps be moved to the daemon or mount. It's here because
// it needs to be accessible across daemon requests.
type Mounts struct {
	Ipfs mount.Mount
	Ipns mount.Mount
}

Jeromy's avatar
Jeromy committed
127
func (n *IpfsNode) startOnlineServices(ctx context.Context, routingOption RoutingOption, hostOption HostOption, do DiscoveryOption) error {
128 129

	if n.PeerHost != nil { // already online.
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
130
		return errors.New("node already online")
131 132 133
	}

	// load private key
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
134
	if err := n.LoadPrivateKey(); err != nil {
135 136 137
		return err
	}

Jeromy's avatar
Jeromy committed
138 139 140
	// Set reporter
	n.Reporter = metrics.NewBandwidthCounter()

Jeromy's avatar
Jeromy committed
141
	// get undialable addrs from config
142 143 144 145
	cfg, err := n.Repo.Config()
	if err != nil {
		return err
	}
Jeromy's avatar
Jeromy committed
146
	var addrfilter []*net.IPNet
147
	for _, s := range cfg.Swarm.AddrFilters {
Jeromy's avatar
Jeromy committed
148 149
		f, err := mamask.NewMask(s)
		if err != nil {
150
			return fmt.Errorf("incorrectly formatted address filter in config: %s", s)
Jeromy's avatar
Jeromy committed
151 152 153 154 155
		}
		addrfilter = append(addrfilter, f)
	}

	peerhost, err := hostOption(ctx, n.Identity, n.Peerstore, n.Reporter, addrfilter)
156
	if err != nil {
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
157
		return err
158 159
	}

160
	if err := n.startOnlineServicesWithHost(ctx, peerhost, routingOption); err != nil {
161
		return err
162 163 164
	}

	// Ok, now we're ready to listen.
165
	if err := startListening(ctx, n.PeerHost, cfg); err != nil {
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
166
		return err
167
	}
168

169
	n.Reprovider = rp.NewReprovider(n.Routing, n.Blockstore)
170
	go n.Reprovider.ProvideEvery(ctx, kReprovideFrequency)
171

172
	// setup local discovery
Jeromy's avatar
Jeromy committed
173 174 175
	if do != nil {
		service, err := do(n.PeerHost)
		if err != nil {
Jeromy's avatar
Jeromy committed
176 177 178 179
			log.Error("mdns error: ", err)
		} else {
			service.RegisterNotifee(n)
			n.Discovery = service
Jeromy's avatar
Jeromy committed
180
		}
181 182
	}

183
	return n.Bootstrap(DefaultBootstrapConfig)
184 185
}

Jeromy's avatar
Jeromy committed
186 187 188 189 190 191 192 193 194 195 196 197
func setupDiscoveryOption(d config.Discovery) DiscoveryOption {
	if d.MDNS.Enabled {
		return func(h p2phost.Host) (discovery.Service, error) {
			if d.MDNS.Interval == 0 {
				d.MDNS.Interval = 5
			}
			return discovery.NewMdnsService(h, time.Duration(d.MDNS.Interval)*time.Second)
		}
	}
	return nil
}

198 199
func (n *IpfsNode) HandlePeerFound(p peer.PeerInfo) {
	log.Warning("trying peer info: ", p)
200
	ctx, cancel := context.WithTimeout(n.Context(), discoveryConnTimeout)
rht's avatar
rht committed
201
	defer cancel()
202
	if err := n.PeerHost.Connect(ctx, p); err != nil {
203 204 205 206
		log.Warning("Failed to connect to peer found by discovery: ", err)
	}
}

207 208
// startOnlineServicesWithHost  is the set of services which need to be
// initialized with the host and _before_ we start listening.
209
func (n *IpfsNode) startOnlineServicesWithHost(ctx context.Context, host p2phost.Host, routingOption RoutingOption) error {
210
	// setup diagnostics service
211
	n.Diagnostics = diag.NewDiagnostics(n.Identity, host)
Jeromy's avatar
Jeromy committed
212
	n.Ping = ping.NewPingService(host)
213 214

	// setup routing service
215
	r, err := routingOption(ctx, host, n.Repo.Datastore())
Jeromy's avatar
Jeromy committed
216
	if err != nil {
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
217
		return err
218
	}
Jeromy's avatar
Jeromy committed
219
	n.Routing = r
220

221 222 223
	// Wrap standard peer host with routing system to allow unknown peer lookups
	n.PeerHost = rhost.Wrap(host, n.Routing)

224 225
	// setup exchange service
	const alwaysSendToPeer = true // use YesManStrategy
226
	bitswapNetwork := bsnet.NewFromIpfsHost(n.PeerHost, n.Routing)
227 228
	n.Exchange = bitswap.New(ctx, n.Identity, bitswapNetwork, n.Blockstore, alwaysSendToPeer)

229 230 231 232 233
	size, err := n.getCacheSize()
	if err != nil {
		return err
	}

234
	// setup name system
235
	n.Namesys = namesys.NewNameSystem(n.Routing, n.Repo.Datastore(), size)
236

Jeromy's avatar
Jeromy committed
237
	// setup ipns republishing
238 239 240 241
	err = n.setupIpnsRepublisher()
	if err != nil {
		return err
	}
Jeromy's avatar
Jeromy committed
242

243 244 245
	return nil
}

246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262
// getCacheSize returns cache life and cache size
func (n *IpfsNode) getCacheSize() (int, error) {
	cfg, err := n.Repo.Config()
	if err != nil {
		return 0, err
	}

	cs := cfg.Ipns.ResolveCacheSize
	if cs == 0 {
		cs = 128
	}
	if cs < 0 {
		return 0, fmt.Errorf("cannot specify negative resolve cache size")
	}
	return cs, nil
}

263
func (n *IpfsNode) setupIpnsRepublisher() error {
Jeromy's avatar
Jeromy committed
264 265 266 267
	cfg, err := n.Repo.Config()
	if err != nil {
		return err
	}
268 269 270 271

	n.IpnsRepub = ipnsrp.NewRepublisher(n.Routing, n.Repo.Datastore(), n.Peerstore)
	n.IpnsRepub.AddName(n.Identity)

Jeromy's avatar
Jeromy committed
272 273 274 275 276 277
	if cfg.Ipns.RepublishPeriod != "" {
		d, err := time.ParseDuration(cfg.Ipns.RepublishPeriod)
		if err != nil {
			return fmt.Errorf("failure to parse config setting IPNS.RepublishPeriod: %s", err)
		}

278
		if !u.Debug && (d < time.Minute || d > (time.Hour*24)) {
Jeromy's avatar
Jeromy committed
279 280 281 282 283 284
			return fmt.Errorf("config setting IPNS.RepublishPeriod is not between 1min and 1day: %s", d)
		}

		n.IpnsRepub.Interval = d
	}

285 286 287 288 289 290 291 292 293
	if cfg.Ipns.RecordLifetime != "" {
		d, err := time.ParseDuration(cfg.Ipns.RepublishPeriod)
		if err != nil {
			return fmt.Errorf("failure to parse config setting IPNS.RecordLifetime: %s", err)
		}

		n.IpnsRepub.RecordLifetime = d
	}

Jeromy's avatar
Jeromy committed
294 295
	n.Process().Go(n.IpnsRepub.Run)

296 297 298
	return nil
}

299 300 301 302 303 304 305 306 307 308 309 310
// Process returns the Process object
func (n *IpfsNode) Process() goprocess.Process {
	return n.proc
}

// Close calls Close() on the Process object
func (n *IpfsNode) Close() error {
	return n.proc.Close()
}

// Context returns the IpfsNode context
func (n *IpfsNode) Context() context.Context {
311 312 313
	if n.ctx == nil {
		n.ctx = context.TODO()
	}
314 315 316
	return n.ctx
}

317 318
// teardown closes owned children. If any errors occur, this function returns
// the first error.
Brian Tiger Chow's avatar
Brian Tiger Chow committed
319
func (n *IpfsNode) teardown() error {
320
	log.Debug("core is shutting down...")
321 322
	// owned objects are closed in this teardown to ensure that they're closed
	// regardless of which constructor was used to add them to the node.
Jeromy's avatar
Jeromy committed
323 324 325
	closers := []io.Closer{
		n.Repo,
	}
326

327 328 329 330
	if n.Exchange != nil {
		closers = append(closers, n.Exchange)
	}

Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
331 332 333 334 335 336 337
	if n.Mounts.Ipfs != nil {
		closers = append(closers, mount.Closer(n.Mounts.Ipfs))
	}
	if n.Mounts.Ipns != nil {
		closers = append(closers, mount.Closer(n.Mounts.Ipns))
	}

Jeromy's avatar
Jeromy committed
338 339
	// Filesystem needs to be closed before network, dht, and blockservice
	// so it can use them as its shutting down
340
	if n.IpnsFs != nil {
Jeromy's avatar
Jeromy committed
341 342 343
		closers = append(closers, n.IpnsFs)
	}

Jeromy's avatar
Jeromy committed
344 345 346 347
	if n.Blocks != nil {
		closers = append(closers, n.Blocks)
	}

Jeromy's avatar
Jeromy committed
348 349
	if n.Bootstrapper != nil {
		closers = append(closers, n.Bootstrapper)
350 351
	}

352
	if dht, ok := n.Routing.(*dht.IpfsDHT); ok {
353
		closers = append(closers, dht.Process())
Jeromy's avatar
Jeromy committed
354 355 356 357
	}

	if n.PeerHost != nil {
		closers = append(closers, n.PeerHost)
358
	}
359

360
	var errs []error
361
	for _, closer := range closers {
362 363
		if err := closer.Close(); err != nil {
			errs = append(errs, err)
364 365 366 367
		}
	}
	if len(errs) > 0 {
		return errs[0]
Brian Tiger Chow's avatar
Brian Tiger Chow committed
368 369
	}
	return nil
Brian Tiger Chow's avatar
Brian Tiger Chow committed
370 371
}

Brian Tiger Chow's avatar
Brian Tiger Chow committed
372
func (n *IpfsNode) OnlineMode() bool {
373 374 375 376 377 378
	switch n.mode {
	case onlineMode:
		return true
	default:
		return false
	}
Brian Tiger Chow's avatar
Brian Tiger Chow committed
379 380
}

381
func (n *IpfsNode) Bootstrap(cfg BootstrapConfig) error {
382 383

	// TODO what should return value be when in offlineMode?
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
384 385 386 387
	if n.Routing == nil {
		return nil
	}

388 389 390 391 392 393 394 395
	if n.Bootstrapper != nil {
		n.Bootstrapper.Close() // stop previous bootstrap process.
	}

	// if the caller did not specify a bootstrap peer function, get the
	// freshest bootstrap peers from config. this responds to live changes.
	if cfg.BootstrapPeers == nil {
		cfg.BootstrapPeers = func() []peer.PeerInfo {
396
			ps, err := n.loadBootstrapPeers()
397
			if err != nil {
398
				log.Warning("failed to parse bootstrap peers from config")
399 400 401 402 403 404 405 406 407
				return nil
			}
			return ps
		}
	}

	var err error
	n.Bootstrapper, err = Bootstrap(n, cfg)
	return err
408 409
}

410 411
func (n *IpfsNode) loadID() error {
	if n.Identity != "" {
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
412
		return errors.New("identity already loaded")
413 414
	}

415 416 417 418 419 420
	cfg, err := n.Repo.Config()
	if err != nil {
		return err
	}

	cid := cfg.Identity.PeerID
421
	if cid == "" {
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
422
		return errors.New("Identity was not set in config (was ipfs init run?)")
423 424
	}
	if len(cid) == 0 {
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
425
		return errors.New("No peer ID in config! (was ipfs init run?)")
426 427
	}

428 429 430
	n.Identity = peer.ID(b58.Decode(cid))
	return nil
}
431

Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
432
func (n *IpfsNode) LoadPrivateKey() error {
433
	if n.Identity == "" || n.Peerstore == nil {
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
434
		return errors.New("loaded private key out of order.")
435 436
	}

437
	if n.PrivateKey != nil {
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
438
		return errors.New("private key already loaded")
439 440
	}

441 442 443 444 445 446
	cfg, err := n.Repo.Config()
	if err != nil {
		return err
	}

	sk, err := loadPrivateKey(&cfg.Identity, n.Identity)
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
447
	if err != nil {
448
		return err
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
449
	}
450

451 452
	n.PrivateKey = sk
	n.Peerstore.AddPrivKey(n.Identity, n.PrivateKey)
Jeromy's avatar
Jeromy committed
453 454 455 456
	n.Peerstore.AddPubKey(n.Identity, sk.GetPublic())
	return nil
}

457
func (n *IpfsNode) loadBootstrapPeers() ([]peer.PeerInfo, error) {
458 459 460 461 462 463
	cfg, err := n.Repo.Config()
	if err != nil {
		return nil, err
	}

	parsed, err := cfg.BootstrapPeers()
464 465 466 467 468 469
	if err != nil {
		return nil, err
	}
	return toPeerInfos(parsed), nil
}

Jeromy's avatar
Jeromy committed
470 471 472
// SetupOfflineRouting loads the local nodes private key and
// uses it to instantiate a routing system in offline mode.
// This is primarily used for offline ipns modifications.
Jeromy's avatar
Jeromy committed
473
func (n *IpfsNode) SetupOfflineRouting() error {
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
474
	err := n.LoadPrivateKey()
Jeromy's avatar
Jeromy committed
475 476 477 478 479
	if err != nil {
		return err
	}

	n.Routing = offroute.NewOfflineRouter(n.Repo.Datastore(), n.PrivateKey)
480

481 482 483 484 485 486
	size, err := n.getCacheSize()
	if err != nil {
		return err
	}

	n.Namesys = namesys.NewNameSystem(n.Routing, n.Repo.Datastore(), size)
487

488
	return nil
489 490 491 492
}

func loadPrivateKey(cfg *config.Identity, id peer.ID) (ic.PrivKey, error) {
	sk, err := cfg.DecodePrivateKey("passphrase todo!")
493 494 495
	if err != nil {
		return nil, err
	}
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
496

497 498 499 500
	id2, err := peer.IDFromPrivateKey(sk)
	if err != nil {
		return nil, err
	}
501

502 503
	if id2 != id {
		return nil, fmt.Errorf("private key in config does not match id: %s != %s", id, id2)
504 505
	}

506
	return sk, nil
507
}
508

509
func listenAddresses(cfg *config.Config) ([]ma.Multiaddr, error) {
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
510 511 512
	var listen []ma.Multiaddr
	for _, addr := range cfg.Addresses.Swarm {
		maddr, err := ma.NewMultiaddr(addr)
513
		if err != nil {
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
514
			return nil, fmt.Errorf("Failure to parse config.Addresses.Swarm: %s", cfg.Addresses.Swarm)
515
		}
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
516
		listen = append(listen, maddr)
517 518 519 520
	}

	return listen, nil
}
521

Jeromy's avatar
Jeromy committed
522
type HostOption func(ctx context.Context, id peer.ID, ps peer.Peerstore, bwr metrics.Reporter, fs []*net.IPNet) (p2phost.Host, error)
Jeromy's avatar
Jeromy committed
523 524 525

var DefaultHostOption HostOption = constructPeerHost

526
// isolates the complex initialization steps
Jeromy's avatar
Jeromy committed
527
func constructPeerHost(ctx context.Context, id peer.ID, ps peer.Peerstore, bwr metrics.Reporter, fs []*net.IPNet) (p2phost.Host, error) {
528 529

	// no addresses to begin with. we'll start later.
Jeromy's avatar
Jeromy committed
530
	network, err := swarm.NewNetwork(ctx, nil, id, ps, bwr)
531
	if err != nil {
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
532
		return nil, err
533
	}
534

535 536 537 538
	for _, f := range fs {
		network.Swarm().Filters.AddDialFilter(f)
	}

Jeromy's avatar
Jeromy committed
539 540
	host := p2pbhost.New(network, p2pbhost.NATPortMap, bwr)

541 542 543 544 545 546 547
	return host, nil
}

// startListening on the network addresses
func startListening(ctx context.Context, host p2phost.Host, cfg *config.Config) error {
	listenAddrs, err := listenAddresses(cfg)
	if err != nil {
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
548
		return err
549 550
	}

551
	// make sure we error out if our config does not have addresses we can use
552
	log.Debugf("Config.Addresses.Swarm:%s", listenAddrs)
553
	filteredAddrs := addrutil.FilterUsableAddrs(listenAddrs)
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
554
	log.Debugf("Config.Addresses.Swarm:%s (filtered)", filteredAddrs)
555
	if len(filteredAddrs) < 1 {
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
556
		return fmt.Errorf("addresses in config not usable: %s", listenAddrs)
557 558
	}

559 560 561
	// Actually start listening:
	if err := host.Network().Listen(filteredAddrs...); err != nil {
		return err
562 563
	}

564
	// list out our addresses
565
	addrs, err := host.Network().InterfaceListenAddresses()
566
	if err != nil {
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
567
		return err
568
	}
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
569
	log.Infof("Swarm listening at: %s", addrs)
570
	return nil
571
}
572

573 574
func constructDHTRouting(ctx context.Context, host p2phost.Host, dstore ds.ThreadSafeDatastore) (routing.IpfsRouting, error) {
	dhtRouting := dht.NewDHT(ctx, host, dstore)
575
	dhtRouting.Validator[IpnsValidatorTag] = namesys.IpnsRecordValidator
576
	dhtRouting.Selector[IpnsValidatorTag] = namesys.IpnsSelectorFunc
577 578
	return dhtRouting, nil
}
Jeromy's avatar
Jeromy committed
579

580
type RoutingOption func(context.Context, p2phost.Host, ds.ThreadSafeDatastore) (routing.IpfsRouting, error)
Jeromy's avatar
Jeromy committed
581

Jeromy's avatar
Jeromy committed
582 583
type DiscoveryOption func(p2phost.Host) (discovery.Service, error)

584
var DHTOption RoutingOption = constructDHTRouting
585
var NilRouterOption RoutingOption = nilrouting.ConstructNilRouting