core.go 15.1 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
	b58 "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-base58"
	ma "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multiaddr"
21
	goprocess "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/goprocess"
Jeromy's avatar
Jeromy committed
22
	mamask "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/whyrusleeping/multiaddr-filter"
23 24
	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
25
	metrics "github.com/ipfs/go-ipfs/metrics"
26
	ic "github.com/ipfs/go-ipfs/p2p/crypto"
27
	discovery "github.com/ipfs/go-ipfs/p2p/discovery"
28 29 30 31 32 33
	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
34
	ping "github.com/ipfs/go-ipfs/p2p/protocol/ping"
Jeromy's avatar
Jeromy committed
35
	logging "github.com/ipfs/go-ipfs/vendor/QmQg1J6vikuXF9oDvm4wpdeAUvvkVEKW1EYDw9HhTMnP2b/go-log"
36

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

42 43 44 45 46 47
	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"
48

49 50 51
	mount "github.com/ipfs/go-ipfs/fuse/mount"
	merkledag "github.com/ipfs/go-ipfs/merkledag"
	namesys "github.com/ipfs/go-ipfs/namesys"
Jeromy's avatar
Jeromy committed
52
	ipnsrp "github.com/ipfs/go-ipfs/namesys/republisher"
53 54 55 56
	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"
57
	u "github.com/ipfs/go-ipfs/util"
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
58 59
)

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

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

67 68 69 70 71 72 73 74 75
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
76
// IpfsNode is IPFS Core module. It represents an IPFS instance.
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
77 78
type IpfsNode struct {

79
	// Self
80
	Identity peer.ID // the local node's identity
81

82
	Repo repo.Repo
83 84

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

	// Services
90
	Peerstore  peer.Peerstore       // storage for other Peer instances
91
	Blockstore bstore.GCBlockstore  // the block store (lower level)
92 93 94
	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
95
	Reporter   metrics.Reporter
96
	Discovery  discovery.Service
97 98

	// Online
99 100 101 102 103 104
	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
105 106
	Ping         *ping.PingService
	Reprovider   *rp.Reprovider // the value reprovider system
Jeromy's avatar
Jeromy committed
107
	IpnsRepub    *ipnsrp.Republisher
108

109 110
	proc goprocess.Process
	ctx  context.Context
111

112
	mode mode
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
113 114
}

115 116 117 118 119 120 121 122
// 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
123
func (n *IpfsNode) startOnlineServices(ctx context.Context, routingOption RoutingOption, hostOption HostOption, do DiscoveryOption) error {
124 125

	if n.PeerHost != nil { // already online.
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
126
		return errors.New("node already online")
127 128 129
	}

	// load private key
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
130
	if err := n.LoadPrivateKey(); err != nil {
131 132 133
		return err
	}

Jeromy's avatar
Jeromy committed
134 135 136
	// Set reporter
	n.Reporter = metrics.NewBandwidthCounter()

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

	peerhost, err := hostOption(ctx, n.Identity, n.Peerstore, n.Reporter, addrfilter)
152
	if err != nil {
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
153
		return err
154 155
	}

156
	if err := n.startOnlineServicesWithHost(ctx, peerhost, routingOption); err != nil {
157
		return err
158 159 160
	}

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

165
	n.Reprovider = rp.NewReprovider(n.Routing, n.Blockstore)
166
	go n.Reprovider.ProvideEvery(ctx, kReprovideFrequency)
167

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

179
	return n.Bootstrap(DefaultBootstrapConfig)
180 181
}

Jeromy's avatar
Jeromy committed
182 183 184 185 186 187 188 189 190 191 192 193
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
}

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

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

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

217 218 219
	// Wrap standard peer host with routing system to allow unknown peer lookups
	n.PeerHost = rhost.Wrap(host, n.Routing)

220 221
	// setup exchange service
	const alwaysSendToPeer = true // use YesManStrategy
222
	bitswapNetwork := bsnet.NewFromIpfsHost(n.PeerHost, n.Routing)
223 224
	n.Exchange = bitswap.New(ctx, n.Identity, bitswapNetwork, n.Blockstore, alwaysSendToPeer)

225 226 227 228 229
	size, err := n.getCacheSize()
	if err != nil {
		return err
	}

230
	// setup name system
231
	n.Namesys = namesys.NewNameSystem(n.Routing, n.Repo.Datastore(), size)
232

Jeromy's avatar
Jeromy committed
233
	// setup ipns republishing
234 235 236 237
	err = n.setupIpnsRepublisher()
	if err != nil {
		return err
	}
Jeromy's avatar
Jeromy committed
238

239 240 241
	return nil
}

242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258
// 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
}

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

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

Jeromy's avatar
Jeromy committed
268 269 270 271 272 273
	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)
		}

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

		n.IpnsRepub.Interval = d
	}

281 282 283 284 285 286 287 288 289
	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
290 291
	n.Process().Go(n.IpnsRepub.Run)

292 293 294
	return nil
}

295 296 297 298 299 300 301 302 303 304 305 306
// 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 {
307 308 309
	if n.ctx == nil {
		n.ctx = context.TODO()
	}
310 311 312
	return n.ctx
}

313 314
// teardown closes owned children. If any errors occur, this function returns
// the first error.
Brian Tiger Chow's avatar
Brian Tiger Chow committed
315
func (n *IpfsNode) teardown() error {
316
	log.Debug("core is shutting down...")
317 318
	// 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
319 320 321
	closers := []io.Closer{
		n.Repo,
	}
322

323 324 325 326
	if n.Exchange != nil {
		closers = append(closers, n.Exchange)
	}

Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
327 328 329 330 331 332 333
	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
334 335 336 337
	if n.Blocks != nil {
		closers = append(closers, n.Blocks)
	}

Jeromy's avatar
Jeromy committed
338 339
	if n.Bootstrapper != nil {
		closers = append(closers, n.Bootstrapper)
340 341
	}

342
	if dht, ok := n.Routing.(*dht.IpfsDHT); ok {
343
		closers = append(closers, dht.Process())
Jeromy's avatar
Jeromy committed
344 345 346 347
	}

	if n.PeerHost != nil {
		closers = append(closers, n.PeerHost)
348
	}
349

350
	var errs []error
351
	for _, closer := range closers {
352 353
		if err := closer.Close(); err != nil {
			errs = append(errs, err)
354 355 356 357
		}
	}
	if len(errs) > 0 {
		return errs[0]
Brian Tiger Chow's avatar
Brian Tiger Chow committed
358 359
	}
	return nil
Brian Tiger Chow's avatar
Brian Tiger Chow committed
360 361
}

Brian Tiger Chow's avatar
Brian Tiger Chow committed
362
func (n *IpfsNode) OnlineMode() bool {
363 364 365 366 367 368
	switch n.mode {
	case onlineMode:
		return true
	default:
		return false
	}
Brian Tiger Chow's avatar
Brian Tiger Chow committed
369 370
}

371
func (n *IpfsNode) Bootstrap(cfg BootstrapConfig) error {
372 373

	// TODO what should return value be when in offlineMode?
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
374 375 376 377
	if n.Routing == nil {
		return nil
	}

378 379 380 381 382 383 384 385
	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 {
386
			ps, err := n.loadBootstrapPeers()
387
			if err != nil {
388
				log.Warning("failed to parse bootstrap peers from config")
389 390 391 392 393 394 395 396 397
				return nil
			}
			return ps
		}
	}

	var err error
	n.Bootstrapper, err = Bootstrap(n, cfg)
	return err
398 399
}

400 401
func (n *IpfsNode) loadID() error {
	if n.Identity != "" {
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
402
		return errors.New("identity already loaded")
403 404
	}

405 406 407 408 409 410
	cfg, err := n.Repo.Config()
	if err != nil {
		return err
	}

	cid := cfg.Identity.PeerID
411
	if cid == "" {
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
412
		return errors.New("Identity was not set in config (was ipfs init run?)")
413 414
	}
	if len(cid) == 0 {
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
415
		return errors.New("No peer ID in config! (was ipfs init run?)")
416 417
	}

418 419 420
	n.Identity = peer.ID(b58.Decode(cid))
	return nil
}
421

Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
422
func (n *IpfsNode) LoadPrivateKey() error {
423
	if n.Identity == "" || n.Peerstore == nil {
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
424
		return errors.New("loaded private key out of order.")
425 426
	}

427
	if n.PrivateKey != nil {
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
428
		return errors.New("private key already loaded")
429 430
	}

431 432 433 434 435 436
	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
437
	if err != nil {
438
		return err
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
439
	}
440

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

447
func (n *IpfsNode) loadBootstrapPeers() ([]peer.PeerInfo, error) {
448 449 450 451 452 453
	cfg, err := n.Repo.Config()
	if err != nil {
		return nil, err
	}

	parsed, err := cfg.BootstrapPeers()
454 455 456 457 458 459
	if err != nil {
		return nil, err
	}
	return toPeerInfos(parsed), nil
}

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

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

471 472 473 474 475 476
	size, err := n.getCacheSize()
	if err != nil {
		return err
	}

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

478
	return nil
479 480 481 482
}

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

487 488 489 490
	id2, err := peer.IDFromPrivateKey(sk)
	if err != nil {
		return nil, err
	}
491

492 493
	if id2 != id {
		return nil, fmt.Errorf("private key in config does not match id: %s != %s", id, id2)
494 495
	}

496
	return sk, nil
497
}
498

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

	return listen, nil
}
511

Jeromy's avatar
Jeromy committed
512
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
513 514 515

var DefaultHostOption HostOption = constructPeerHost

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

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

525 526 527 528
	for _, f := range fs {
		network.Swarm().Filters.AddDialFilter(f)
	}

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

531 532 533 534 535 536 537
	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
538
		return err
539 540
	}

541
	// make sure we error out if our config does not have addresses we can use
542
	log.Debugf("Config.Addresses.Swarm:%s", listenAddrs)
543
	filteredAddrs := addrutil.FilterUsableAddrs(listenAddrs)
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
544
	log.Debugf("Config.Addresses.Swarm:%s (filtered)", filteredAddrs)
545
	if len(filteredAddrs) < 1 {
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
546
		return fmt.Errorf("addresses in config not usable: %s", listenAddrs)
547 548
	}

549 550 551
	// Actually start listening:
	if err := host.Network().Listen(filteredAddrs...); err != nil {
		return err
552 553
	}

554
	// list out our addresses
555
	addrs, err := host.Network().InterfaceListenAddresses()
556
	if err != nil {
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
557
		return err
558
	}
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
559
	log.Infof("Swarm listening at: %s", addrs)
560
	return nil
561
}
562

Jeromy's avatar
Jeromy committed
563
func constructDHTRouting(ctx context.Context, host p2phost.Host, dstore repo.Datastore) (routing.IpfsRouting, error) {
564
	dhtRouting := dht.NewDHT(ctx, host, dstore)
565
	dhtRouting.Validator[IpnsValidatorTag] = namesys.IpnsRecordValidator
566
	dhtRouting.Selector[IpnsValidatorTag] = namesys.IpnsSelectorFunc
567 568
	return dhtRouting, nil
}
Jeromy's avatar
Jeromy committed
569

Jeromy's avatar
Jeromy committed
570
type RoutingOption func(context.Context, p2phost.Host, repo.Datastore) (routing.IpfsRouting, error)
Jeromy's avatar
Jeromy committed
571

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

574
var DHTOption RoutingOption = constructDHTRouting
575
var NilRouterOption RoutingOption = nilrouting.ConstructNilRouting