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

Packages underneath core/ provide a (relatively) stable, low-level API
to carry out most IPFS-related tasks.
*/
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
7 8
package core

Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
9
import (
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
10
	"errors"
Juan Batiz-Benet's avatar
go fmt  
Juan Batiz-Benet committed
11
	"fmt"
12
	"io"
13
	"time"
14

15 16 17 18 19
	b58 "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-base58"
	ctxgroup "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-ctxgroup"
	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"
	context "github.com/ipfs/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context"
Jeromy's avatar
Jeromy committed
20
	metrics "github.com/ipfs/go-ipfs/metrics"
21
	eventlog "github.com/ipfs/go-ipfs/thirdparty/eventlog"
22

23 24
	diag "github.com/ipfs/go-ipfs/diagnostics"
	ic "github.com/ipfs/go-ipfs/p2p/crypto"
25
	discovery "github.com/ipfs/go-ipfs/p2p/discovery"
26 27 28 29 30 31
	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"
32

33 34 35 36
	routing "github.com/ipfs/go-ipfs/routing"
	dht "github.com/ipfs/go-ipfs/routing/dht"
	kb "github.com/ipfs/go-ipfs/routing/kbucket"
	offroute "github.com/ipfs/go-ipfs/routing/offline"
37

38 39 40 41 42 43 44
	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"
	offline "github.com/ipfs/go-ipfs/exchange/offline"
	rp "github.com/ipfs/go-ipfs/exchange/reprovide"
45

46 47 48 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"
	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"
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
54 55
)

Jeromy's avatar
Jeromy committed
56
const IpnsValidatorTag = "ipns"
57
const kSizeBlockstoreWriteCache = 100
58
const kReprovideFrequency = time.Hour * 12
Jeromy's avatar
Jeromy committed
59

Brian Tiger Chow's avatar
Brian Tiger Chow committed
60
var log = eventlog.Logger("core")
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
61

62 63 64 65 66 67 68 69 70
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
71
// IpfsNode is IPFS Core module. It represents an IPFS instance.
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
72 73
type IpfsNode struct {

74
	// Self
75
	Identity peer.ID // the local node's identity
76

77
	Repo repo.Repo
78 79

	// Local node
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
80 81 82
	Pinning    pin.Pinner // the pinning manager
	Mounts     Mounts     // current mount state, if any.
	PrivateKey ic.PrivKey // the local node's private Key
83 84

	// Services
85 86 87 88 89
	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
90
	Reporter   metrics.Reporter
91
	Discovery  discovery.Service
92 93

	// Online
94 95 96 97 98 99 100
	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
	Reprovider   *rp.Reprovider      // the value reprovider system
101

102 103
	IpnsFs *ipnsfs.Filesystem

104
	ctxgroup.ContextGroup
105

106
	mode mode
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
107 108
}

109 110 111 112 113 114 115 116
// 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
}

117
type ConfigOption func(ctx context.Context) (*IpfsNode, error)
118

119 120 121 122 123 124 125 126 127 128
func NewIPFSNode(parent context.Context, option ConfigOption) (*IpfsNode, error) {
	ctxg := ctxgroup.WithContext(parent)
	ctx := ctxg.Context()
	success := false // flip to true after all sub-system inits succeed
	defer func() {
		if !success {
			ctxg.Close()
		}
	}()

Brian Tiger Chow's avatar
Brian Tiger Chow committed
129
	node, err := option(ctx)
130 131 132
	if err != nil {
		return nil, err
	}
133 134
	node.ContextGroup = ctxg
	ctxg.SetTeardown(node.teardown)
135 136 137 138 139

	// Need to make sure it's perfectly clear 1) which variables are expected
	// to be initialized at this point, and 2) which variables will be
	// initialized after this point.

140 141
	node.Blocks, err = bserv.New(node.Blockstore, node.Exchange)
	if err != nil {
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
142
		return nil, err
143
	}
144 145 146
	if node.Peerstore == nil {
		node.Peerstore = peer.NewPeerstore()
	}
147
	node.DAG = merkledag.NewDAGService(node.Blocks)
148
	node.Pinning, err = pin.LoadPinner(node.Repo.Datastore(), node.DAG)
149
	if err != nil {
150
		node.Pinning = pin.NewPinner(node.Repo.Datastore(), node.DAG)
151 152
	}
	node.Resolver = &path.Resolver{DAG: node.DAG}
153 154 155 156

	// Setup the mutable ipns filesystem structure
	if node.OnlineMode() {
		fs, err := ipnsfs.NewFilesystem(ctx, node.DAG, node.Namesys, node.Pinning, node.PrivateKey)
157
		if err != nil && err != kb.ErrLookupFailure {
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
158
			return nil, err
159 160 161 162
		}
		node.IpnsFs = fs
	}

163
	success = true
Brian Tiger Chow's avatar
Brian Tiger Chow committed
164
	return node, nil
165 166
}

167 168
func Offline(r repo.Repo) ConfigOption {
	return Standard(r, false)
169 170
}

Jeromy's avatar
Jeromy committed
171 172
func OnlineWithOptions(r repo.Repo, router RoutingOption, ho HostOption) ConfigOption {
	return standardWithRouting(r, true, router, ho)
173 174
}

175 176
func Online(r repo.Repo) ConfigOption {
	return Standard(r, true)
177 178 179
}

// DEPRECATED: use Online, Offline functions
180
func Standard(r repo.Repo, online bool) ConfigOption {
Jeromy's avatar
Jeromy committed
181
	return standardWithRouting(r, online, DHTOption, DefaultHostOption)
182 183 184
}

// TODO refactor so maybeRouter isn't special-cased in this way
Jeromy's avatar
Jeromy committed
185
func standardWithRouting(r repo.Repo, online bool, routingOption RoutingOption, hostOption HostOption) ConfigOption {
186
	return func(ctx context.Context) (n *IpfsNode, err error) {
Brian Tiger Chow's avatar
Brian Tiger Chow committed
187 188 189 190 191 192 193 194 195 196 197 198
		// FIXME perform node construction in the main constructor so it isn't
		// necessary to perform this teardown in this scope.
		success := false
		defer func() {
			if !success && n != nil {
				n.teardown()
			}
		}()

		// TODO move as much of node initialization as possible into
		// NewIPFSNode. The larger these config options are, the harder it is
		// to test all node construction code paths.
199

200
		if r == nil {
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
201
			return nil, fmt.Errorf("repo required")
202 203
		}
		n = &IpfsNode{
204 205 206 207 208 209
			mode: func() mode {
				if online {
					return onlineMode
				}
				return offlineMode
			}(),
210
			Repo: r,
211
		}
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
212

213 214
		// setup Peerstore
		n.Peerstore = peer.NewPeerstore()
215

216 217 218 219
		// setup local peer ID (private key is loaded in online setup)
		if err := n.loadID(); err != nil {
			return nil, err
		}
Juan Batiz-Benet's avatar
go fmt  
Juan Batiz-Benet committed
220

221
		n.Blockstore, err = bstore.WriteCached(bstore.NewBlockstore(n.Repo.Datastore()), kSizeBlockstoreWriteCache)
222
		if err != nil {
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
223
			return nil, err
224
		}
225

226
		if online {
Jeromy's avatar
Jeromy committed
227 228
			do := setupDiscoveryOption(n.Repo.Config().Discovery)
			if err := n.startOnlineServices(ctx, routingOption, hostOption, do); err != nil {
Brian Tiger Chow's avatar
Brian Tiger Chow committed
229
				return nil, err
230 231 232 233
			}
		} else {
			n.Exchange = offline.Exchange(n.Blockstore)
		}
234

Brian Tiger Chow's avatar
Brian Tiger Chow committed
235
		success = true
236
		return n, nil
Jeromy's avatar
Jeromy committed
237
	}
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
238
}
239

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

	if n.PeerHost != nil { // already online.
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
243
		return errors.New("node already online")
244 245 246
	}

	// load private key
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
247
	if err := n.LoadPrivateKey(); err != nil {
248 249 250
		return err
	}

Jeromy's avatar
Jeromy committed
251 252 253 254
	// Set reporter
	n.Reporter = metrics.NewBandwidthCounter()

	peerhost, err := hostOption(ctx, n.Identity, n.Peerstore, n.Reporter)
255
	if err != nil {
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
256
		return err
257 258
	}

259
	if err := n.startOnlineServicesWithHost(ctx, peerhost, routingOption); err != nil {
260
		return err
261 262 263 264
	}

	// Ok, now we're ready to listen.
	if err := startListening(ctx, n.PeerHost, n.Repo.Config()); err != nil {
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
265
		return err
266
	}
267

268
	n.Reprovider = rp.NewReprovider(n.Routing, n.Blockstore)
269
	go n.Reprovider.ProvideEvery(ctx, kReprovideFrequency)
270

271
	// setup local discovery
Jeromy's avatar
Jeromy committed
272 273 274
	if do != nil {
		service, err := do(n.PeerHost)
		if err != nil {
Jeromy's avatar
Jeromy committed
275 276 277 278
			log.Error("mdns error: ", err)
		} else {
			service.RegisterNotifee(n)
			n.Discovery = service
Jeromy's avatar
Jeromy committed
279
		}
280 281
	}

282
	return n.Bootstrap(DefaultBootstrapConfig)
283 284
}

Jeromy's avatar
Jeromy committed
285 286 287 288 289 290 291 292 293 294 295 296
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
}

297 298
func (n *IpfsNode) HandlePeerFound(p peer.PeerInfo) {
	log.Warning("trying peer info: ", p)
299
	ctx, _ := context.WithTimeout(context.TODO(), time.Second*10)
300 301 302 303 304 305
	err := n.PeerHost.Connect(ctx, p)
	if err != nil {
		log.Warning("Failed to connect to peer found by discovery: ", err)
	}
}

306 307
// startOnlineServicesWithHost  is the set of services which need to be
// initialized with the host and _before_ we start listening.
308
func (n *IpfsNode) startOnlineServicesWithHost(ctx context.Context, host p2phost.Host, routingOption RoutingOption) error {
309
	// setup diagnostics service
310
	n.Diagnostics = diag.NewDiagnostics(n.Identity, host)
311 312

	// setup routing service
313
	r, err := routingOption(ctx, host, n.Repo.Datastore())
Jeromy's avatar
Jeromy committed
314
	if err != nil {
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
315
		return err
316
	}
Jeromy's avatar
Jeromy committed
317
	n.Routing = r
318

319 320 321
	// Wrap standard peer host with routing system to allow unknown peer lookups
	n.PeerHost = rhost.Wrap(host, n.Routing)

322 323
	// setup exchange service
	const alwaysSendToPeer = true // use YesManStrategy
324
	bitswapNetwork := bsnet.NewFromIpfsHost(n.PeerHost, n.Routing)
325 326 327 328
	n.Exchange = bitswap.New(ctx, n.Identity, bitswapNetwork, n.Blockstore, alwaysSendToPeer)

	// setup name system
	n.Namesys = namesys.NewNameSystem(n.Routing)
329

330 331 332
	return nil
}

333 334
// teardown closes owned children. If any errors occur, this function returns
// the first error.
Brian Tiger Chow's avatar
Brian Tiger Chow committed
335
func (n *IpfsNode) teardown() error {
336
	log.Debug("core is shutting down...")
337 338
	// 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
339 340 341 342
	closers := []io.Closer{
		n.Exchange,
		n.Repo,
	}
343

Jeromy's avatar
Jeromy committed
344 345
	// Filesystem needs to be closed before network, dht, and blockservice
	// so it can use them as its shutting down
346
	if n.IpnsFs != nil {
Jeromy's avatar
Jeromy committed
347 348 349
		closers = append(closers, n.IpnsFs)
	}

Jeromy's avatar
Jeromy committed
350 351 352 353
	if n.Blocks != nil {
		closers = append(closers, n.Blocks)
	}

Jeromy's avatar
Jeromy committed
354 355
	if n.Bootstrapper != nil {
		closers = append(closers, n.Bootstrapper)
356 357
	}

358
	if dht, ok := n.Routing.(*dht.IpfsDHT); ok {
Jeromy's avatar
Jeromy committed
359 360 361 362 363
		closers = append(closers, dht)
	}

	if n.PeerHost != nil {
		closers = append(closers, n.PeerHost)
364
	}
365

366
	var errs []error
367
	for _, closer := range closers {
368 369
		if err := closer.Close(); err != nil {
			errs = append(errs, err)
370 371 372 373
		}
	}
	if len(errs) > 0 {
		return errs[0]
Brian Tiger Chow's avatar
Brian Tiger Chow committed
374 375
	}
	return nil
Brian Tiger Chow's avatar
Brian Tiger Chow committed
376 377
}

Brian Tiger Chow's avatar
Brian Tiger Chow committed
378
func (n *IpfsNode) OnlineMode() bool {
379 380 381 382 383 384
	switch n.mode {
	case onlineMode:
		return true
	default:
		return false
	}
Brian Tiger Chow's avatar
Brian Tiger Chow committed
385 386
}

387
func (n *IpfsNode) Bootstrap(cfg BootstrapConfig) error {
388 389

	// TODO what should return value be when in offlineMode?
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
390 391 392 393
	if n.Routing == nil {
		return nil
	}

394 395 396 397 398 399 400 401
	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 {
402
			ps, err := n.loadBootstrapPeers()
403
			if err != nil {
404
				log.Warningf("failed to parse bootstrap peers from config: %s", n.Repo.Config().Bootstrap)
405 406 407 408 409 410 411 412 413
				return nil
			}
			return ps
		}
	}

	var err error
	n.Bootstrapper, err = Bootstrap(n, cfg)
	return err
414 415
}

416 417
func (n *IpfsNode) loadID() error {
	if n.Identity != "" {
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
418
		return errors.New("identity already loaded")
419 420
	}

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

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

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

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

442
	sk, err := loadPrivateKey(&n.Repo.Config().Identity, n.Identity)
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
443
	if err != nil {
444
		return err
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
445
	}
446

447 448
	n.PrivateKey = sk
	n.Peerstore.AddPrivKey(n.Identity, n.PrivateKey)
Jeromy's avatar
Jeromy committed
449 450 451 452
	n.Peerstore.AddPubKey(n.Identity, sk.GetPublic())
	return nil
}

453 454 455 456 457 458 459 460
func (n *IpfsNode) loadBootstrapPeers() ([]peer.PeerInfo, error) {
	parsed, err := n.Repo.Config().BootstrapPeers()
	if err != nil {
		return nil, err
	}
	return toPeerInfos(parsed), nil
}

Jeromy's avatar
Jeromy committed
461 462 463
// 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
464
func (n *IpfsNode) SetupOfflineRouting() error {
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
465
	err := n.LoadPrivateKey()
Jeromy's avatar
Jeromy committed
466 467 468 469 470
	if err != nil {
		return err
	}

	n.Routing = offroute.NewOfflineRouter(n.Repo.Datastore(), n.PrivateKey)
471 472 473

	n.Namesys = namesys.NewNameSystem(n.Routing)

474
	return nil
475 476 477 478
}

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

483 484 485 486
	id2, err := peer.IDFromPrivateKey(sk)
	if err != nil {
		return nil, err
	}
487

488 489
	if id2 != id {
		return nil, fmt.Errorf("private key in config does not match id: %s != %s", id, id2)
490 491
	}

492
	return sk, nil
493
}
494

495
func listenAddresses(cfg *config.Config) ([]ma.Multiaddr, error) {
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
496 497 498
	var listen []ma.Multiaddr
	for _, addr := range cfg.Addresses.Swarm {
		maddr, err := ma.NewMultiaddr(addr)
499
		if err != nil {
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
500
			return nil, fmt.Errorf("Failure to parse config.Addresses.Swarm: %s", cfg.Addresses.Swarm)
501
		}
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
502
		listen = append(listen, maddr)
503 504 505 506
	}

	return listen, nil
}
507

Jeromy's avatar
Jeromy committed
508
type HostOption func(ctx context.Context, id peer.ID, ps peer.Peerstore, bwr metrics.Reporter) (p2phost.Host, error)
Jeromy's avatar
Jeromy committed
509 510 511

var DefaultHostOption HostOption = constructPeerHost

512
// isolates the complex initialization steps
Jeromy's avatar
Jeromy committed
513
func constructPeerHost(ctx context.Context, id peer.ID, ps peer.Peerstore, bwr metrics.Reporter) (p2phost.Host, error) {
514 515

	// no addresses to begin with. we'll start later.
Jeromy's avatar
Jeromy committed
516
	network, err := swarm.NewNetwork(ctx, nil, id, ps, bwr)
517
	if err != nil {
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
518
		return nil, err
519
	}
520

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

523 524 525 526 527 528 529
	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
530
		return err
531 532
	}

533
	// make sure we error out if our config does not have addresses we can use
534
	log.Debugf("Config.Addresses.Swarm:%s", listenAddrs)
535
	filteredAddrs := addrutil.FilterUsableAddrs(listenAddrs)
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
536
	log.Debugf("Config.Addresses.Swarm:%s (filtered)", filteredAddrs)
537
	if len(filteredAddrs) < 1 {
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
538
		return fmt.Errorf("addresses in config not usable: %s", listenAddrs)
539 540
	}

541 542 543
	// Actually start listening:
	if err := host.Network().Listen(filteredAddrs...); err != nil {
		return err
544 545
	}

546
	// list out our addresses
547
	addrs, err := host.Network().InterfaceListenAddresses()
548
	if err != nil {
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
549
		return err
550
	}
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
551
	log.Infof("Swarm listening at: %s", addrs)
552
	return nil
553
}
554

555 556
func constructDHTRouting(ctx context.Context, host p2phost.Host, dstore ds.ThreadSafeDatastore) (routing.IpfsRouting, error) {
	dhtRouting := dht.NewDHT(ctx, host, dstore)
557
	dhtRouting.Validator[IpnsValidatorTag] = namesys.IpnsRecordValidator
558 559
	return dhtRouting, nil
}
Jeromy's avatar
Jeromy committed
560

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

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

565
var DHTOption RoutingOption = constructDHTRouting