core.go 15.3 KB
Newer Older
Jeromy's avatar
Jeromy committed
1 2
// package core implements the IpfsNode object and methods for constructing
// and properly setting it up.
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
3 4
package core

Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
5
import (
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
6
	"errors"
Juan Batiz-Benet's avatar
go fmt  
Juan Batiz-Benet committed
7
	"fmt"
8
	"io"
9
	"time"
10

11 12 13 14 15
	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
16
	metrics "github.com/ipfs/go-ipfs/metrics"
17
	eventlog "github.com/ipfs/go-ipfs/thirdparty/eventlog"
18

19 20
	diag "github.com/ipfs/go-ipfs/diagnostics"
	ic "github.com/ipfs/go-ipfs/p2p/crypto"
21
	discovery "github.com/ipfs/go-ipfs/p2p/discovery"
22 23 24 25 26 27
	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"
28

29 30 31 32
	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"
33

34 35 36 37 38 39 40
	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"
41

42 43 44 45 46 47 48 49
	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
50 51
)

Jeromy's avatar
Jeromy committed
52
const IpnsValidatorTag = "ipns"
53
const kSizeBlockstoreWriteCache = 100
54
const kReprovideFrequency = time.Hour * 12
Jeromy's avatar
Jeromy committed
55

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

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

70
	// Self
71
	Identity peer.ID // the local node's identity
72

73
	Repo repo.Repo
74 75

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

	// Services
81 82 83 84 85
	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
86
	Reporter   metrics.Reporter
87
	Discovery  discovery.Service
88 89

	// Online
90 91 92 93 94 95 96
	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
97

98 99
	IpnsFs *ipnsfs.Filesystem

100
	ctxgroup.ContextGroup
101

102
	mode mode
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
103 104
}

105 106 107 108 109 110 111 112
// 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
}

113
type ConfigOption func(ctx context.Context) (*IpfsNode, error)
114

115 116 117 118 119 120 121 122 123 124
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
125
	node, err := option(ctx)
126 127 128
	if err != nil {
		return nil, err
	}
129 130
	node.ContextGroup = ctxg
	ctxg.SetTeardown(node.teardown)
131 132 133 134 135

	// 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.

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

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

159
	success = true
Brian Tiger Chow's avatar
Brian Tiger Chow committed
160
	return node, nil
161 162
}

163 164
func Offline(r repo.Repo) ConfigOption {
	return Standard(r, false)
165 166
}

Jeromy's avatar
Jeromy committed
167 168
func OnlineWithOptions(r repo.Repo, router RoutingOption, ho HostOption) ConfigOption {
	return standardWithRouting(r, true, router, ho)
169 170
}

171 172
func Online(r repo.Repo) ConfigOption {
	return Standard(r, true)
173 174 175
}

// DEPRECATED: use Online, Offline functions
176
func Standard(r repo.Repo, online bool) ConfigOption {
Jeromy's avatar
Jeromy committed
177
	return standardWithRouting(r, online, DHTOption, DefaultHostOption)
178 179 180
}

// TODO refactor so maybeRouter isn't special-cased in this way
Jeromy's avatar
Jeromy committed
181
func standardWithRouting(r repo.Repo, online bool, routingOption RoutingOption, hostOption HostOption) ConfigOption {
182
	return func(ctx context.Context) (n *IpfsNode, err error) {
Brian Tiger Chow's avatar
Brian Tiger Chow committed
183 184 185 186 187 188 189 190 191 192 193 194
		// 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.
195

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

209 210
		// setup Peerstore
		n.Peerstore = peer.NewPeerstore()
211

212 213 214 215
		// 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
216

217
		n.Blockstore, err = bstore.WriteCached(bstore.NewBlockstore(n.Repo.Datastore()), kSizeBlockstoreWriteCache)
218
		if err != nil {
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
219
			return nil, err
220
		}
221

222
		if online {
Jeromy's avatar
Jeromy committed
223 224
			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
225
				return nil, err
226 227 228 229
			}
		} else {
			n.Exchange = offline.Exchange(n.Blockstore)
		}
230

Brian Tiger Chow's avatar
Brian Tiger Chow committed
231
		success = true
232
		return n, nil
Jeromy's avatar
Jeromy committed
233
	}
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
234
}
235

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

	if n.PeerHost != nil { // already online.
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
239
		return errors.New("node already online")
240 241 242
	}

	// load private key
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
243
	if err := n.LoadPrivateKey(); err != nil {
244 245 246
		return err
	}

Jeromy's avatar
Jeromy committed
247 248 249 250
	// Set reporter
	n.Reporter = metrics.NewBandwidthCounter()

	peerhost, err := hostOption(ctx, n.Identity, n.Peerstore, n.Reporter)
251
	if err != nil {
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
252
		return err
253 254
	}

255
	if err := n.startOnlineServicesWithHost(ctx, peerhost, routingOption); err != nil {
256
		return err
257 258 259 260
	}

	// 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
261
		return err
262
	}
263

264
	n.Reprovider = rp.NewReprovider(n.Routing, n.Blockstore)
265
	go n.Reprovider.ProvideEvery(ctx, kReprovideFrequency)
266

267
	// setup local discovery
Jeromy's avatar
Jeromy committed
268 269 270 271 272 273 274
	if do != nil {
		service, err := do(n.PeerHost)
		if err != nil {
			return err
		}
		service.RegisterNotifee(n)
		n.Discovery = service
275 276
	}

277
	return n.Bootstrap(DefaultBootstrapConfig)
278 279
}

Jeromy's avatar
Jeromy committed
280 281 282 283 284 285 286 287 288 289 290 291
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
}

292 293 294 295 296 297 298 299 300
func (n *IpfsNode) HandlePeerFound(p peer.PeerInfo) {
	log.Warning("trying peer info: ", p)
	ctx, _ := context.WithTimeout(n.Context(), time.Second*10)
	err := n.PeerHost.Connect(ctx, p)
	if err != nil {
		log.Warning("Failed to connect to peer found by discovery: ", err)
	}
}

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

	// setup routing service
308
	r, err := routingOption(ctx, host, n.Repo.Datastore())
Jeromy's avatar
Jeromy committed
309
	if err != nil {
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
310
		return err
311
	}
Jeromy's avatar
Jeromy committed
312
	n.Routing = r
313

314 315 316
	// Wrap standard peer host with routing system to allow unknown peer lookups
	n.PeerHost = rhost.Wrap(host, n.Routing)

317 318
	// setup exchange service
	const alwaysSendToPeer = true // use YesManStrategy
319
	bitswapNetwork := bsnet.NewFromIpfsHost(n.PeerHost, n.Routing)
320 321 322 323
	n.Exchange = bitswap.New(ctx, n.Identity, bitswapNetwork, n.Blockstore, alwaysSendToPeer)

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

325 326 327
	return nil
}

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

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

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

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

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

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

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

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

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

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

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

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

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

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

424 425 426
	n.Identity = peer.ID(b58.Decode(cid))
	return nil
}
427

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

433
	if n.PrivateKey != nil {
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
434
		return errors.New("private key already loaded")
435 436
	}

437
	sk, err := loadPrivateKey(&n.Repo.Config().Identity, n.Identity)
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
438
	if err != nil {
439
		return err
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
440
	}
441

442 443
	n.PrivateKey = sk
	n.Peerstore.AddPrivKey(n.Identity, n.PrivateKey)
Jeromy's avatar
Jeromy committed
444 445 446 447
	n.Peerstore.AddPubKey(n.Identity, sk.GetPublic())
	return nil
}

448 449 450 451 452 453 454 455
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
456 457 458
// 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
459
func (n *IpfsNode) SetupOfflineRouting() error {
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
460
	err := n.LoadPrivateKey()
Jeromy's avatar
Jeromy committed
461 462 463 464 465
	if err != nil {
		return err
	}

	n.Routing = offroute.NewOfflineRouter(n.Repo.Datastore(), n.PrivateKey)
466 467 468

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

469
	return nil
470 471 472 473
}

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

478 479 480 481
	id2, err := peer.IDFromPrivateKey(sk)
	if err != nil {
		return nil, err
	}
482

483 484
	if id2 != id {
		return nil, fmt.Errorf("private key in config does not match id: %s != %s", id, id2)
485 486
	}

487
	return sk, nil
488
}
489

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

	return listen, nil
}
502

Jeromy's avatar
Jeromy committed
503
type HostOption func(ctx context.Context, id peer.ID, ps peer.Peerstore, bwr metrics.Reporter) (p2phost.Host, error)
Jeromy's avatar
Jeromy committed
504 505 506

var DefaultHostOption HostOption = constructPeerHost

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

	// no addresses to begin with. we'll start later.
Jeromy's avatar
Jeromy committed
511
	network, err := swarm.NewNetwork(ctx, nil, id, ps, bwr)
512
	if err != nil {
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
513
		return nil, err
514
	}
515

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

518 519 520 521 522 523 524
	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
525
		return err
526 527
	}

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

536 537 538
	// Actually start listening:
	if err := host.Network().Listen(filteredAddrs...); err != nil {
		return err
539 540
	}

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

550 551
func constructDHTRouting(ctx context.Context, host p2phost.Host, dstore ds.ThreadSafeDatastore) (routing.IpfsRouting, error) {
	dhtRouting := dht.NewDHT(ctx, host, dstore)
552
	dhtRouting.Validator[IpnsValidatorTag] = namesys.IpnsRecordValidator
553 554
	return dhtRouting, nil
}
Jeromy's avatar
Jeromy committed
555

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

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

560
var DHTOption RoutingOption = constructDHTRouting