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"
16
	"time"
17

18 19 20 21 22
	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
23
	metrics "github.com/ipfs/go-ipfs/metrics"
24
	eventlog "github.com/ipfs/go-ipfs/thirdparty/eventlog"
25

26 27
	diag "github.com/ipfs/go-ipfs/diagnostics"
	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"
35

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

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"
	offline "github.com/ipfs/go-ipfs/exchange/offline"
	rp "github.com/ipfs/go-ipfs/exchange/reprovide"
48

49 50 51 52 53 54 55 56
	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
57 58
)

Jeromy's avatar
Jeromy committed
59
const IpnsValidatorTag = "ipns"
60
const kSizeBlockstoreWriteCache = 100
61
const kReprovideFrequency = time.Hour * 12
Jeromy's avatar
Jeromy committed
62

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

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

77
	// Self
78
	Identity peer.ID // the local node's identity
79

80
	Repo repo.Repo
81 82

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

	// Services
88 89 90 91 92
	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
93
	Reporter   metrics.Reporter
94
	Discovery  discovery.Service
95 96

	// Online
97 98 99 100 101 102 103
	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
104

105 106
	IpnsFs *ipnsfs.Filesystem

107
	ctxgroup.ContextGroup
108

109
	mode mode
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
110 111
}

112 113 114 115 116 117 118 119
// 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
}

120
type ConfigOption func(ctx context.Context) (*IpfsNode, error)
121

122 123 124 125 126 127 128 129 130 131
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
132
	node, err := option(ctx)
133 134 135
	if err != nil {
		return nil, err
	}
136 137
	node.ContextGroup = ctxg
	ctxg.SetTeardown(node.teardown)
138 139 140 141 142

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

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

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

166
	success = true
Brian Tiger Chow's avatar
Brian Tiger Chow committed
167
	return node, nil
168 169
}

170 171
func Offline(r repo.Repo) ConfigOption {
	return Standard(r, false)
172 173
}

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

178 179
func Online(r repo.Repo) ConfigOption {
	return Standard(r, true)
180 181 182
}

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

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

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

216 217
		// setup Peerstore
		n.Peerstore = peer.NewPeerstore()
218

219 220 221 222
		// 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
223

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

229
		if online {
Jeromy's avatar
Jeromy committed
230 231
			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
232
				return nil, err
233 234 235 236
			}
		} else {
			n.Exchange = offline.Exchange(n.Blockstore)
		}
237

Brian Tiger Chow's avatar
Brian Tiger Chow committed
238
		success = true
239
		return n, nil
Jeromy's avatar
Jeromy committed
240
	}
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
241
}
242

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

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

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

Jeromy's avatar
Jeromy committed
254 255 256 257
	// Set reporter
	n.Reporter = metrics.NewBandwidthCounter()

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

262
	if err := n.startOnlineServicesWithHost(ctx, peerhost, routingOption); err != nil {
263
		return err
264 265 266 267
	}

	// 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
268
		return err
269
	}
270

271
	n.Reprovider = rp.NewReprovider(n.Routing, n.Blockstore)
272
	go n.Reprovider.ProvideEvery(ctx, kReprovideFrequency)
273

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

285
	return n.Bootstrap(DefaultBootstrapConfig)
286 287
}

Jeromy's avatar
Jeromy committed
288 289 290 291 292 293 294 295 296 297 298 299
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
}

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

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

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

322 323 324
	// Wrap standard peer host with routing system to allow unknown peer lookups
	n.PeerHost = rhost.Wrap(host, n.Routing)

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

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

333 334 335
	return nil
}

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

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

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

Jeromy's avatar
Jeromy committed
357 358
	if n.Bootstrapper != nil {
		closers = append(closers, n.Bootstrapper)
359 360
	}

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

	if n.PeerHost != nil {
		closers = append(closers, n.PeerHost)
367
	}
368

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

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

390
func (n *IpfsNode) Bootstrap(cfg BootstrapConfig) error {
391 392

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

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

	var err error
	n.Bootstrapper, err = Bootstrap(n, cfg)
	return err
417 418
}

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

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

432 433 434
	n.Identity = peer.ID(b58.Decode(cid))
	return nil
}
435

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

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

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

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

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

	n.Routing = offroute.NewOfflineRouter(n.Repo.Datastore(), n.PrivateKey)
474 475 476

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

477
	return nil
478 479 480 481
}

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

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

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

495
	return sk, nil
496
}
497

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

	return listen, nil
}
510

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

var DefaultHostOption HostOption = constructPeerHost

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

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

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

526 527 528 529 530 531 532
	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
533
		return err
534 535
	}

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

544 545 546
	// Actually start listening:
	if err := host.Network().Listen(filteredAddrs...); err != nil {
		return err
547 548
	}

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

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

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

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

568
var DHTOption RoutingOption = constructDHTRouting