core.go 16 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 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"
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
	eventlog "github.com/ipfs/go-ipfs/thirdparty/eventlog"
36

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

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

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

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

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

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

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

81
	Repo repo.Repo
82 83

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

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

	// Online
98 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
	Reprovider   *rp.Reprovider      // the value reprovider system
105

106 107
	IpnsFs *ipnsfs.Filesystem

108
	ctxgroup.ContextGroup
109

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Jeromy's avatar
Jeromy committed
258 259 260
	// get undialable addrs from config
	cfg := n.Repo.Config()
	var addrfilter []*net.IPNet
261
	for _, s := range cfg.Swarm.AddrFilters {
Jeromy's avatar
Jeromy committed
262 263 264 265 266 267 268 269
		f, err := mamask.NewMask(s)
		if err != nil {
			return fmt.Errorf("incorrectly formatter address filter in config: %s", s)
		}
		addrfilter = append(addrfilter, f)
	}

	peerhost, err := hostOption(ctx, n.Identity, n.Peerstore, n.Reporter, addrfilter)
270
	if err != nil {
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
271
		return err
272 273
	}

274
	if err := n.startOnlineServicesWithHost(ctx, peerhost, routingOption); err != nil {
275
		return err
276 277 278 279
	}

	// 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
280
		return err
281
	}
282

283
	n.Reprovider = rp.NewReprovider(n.Routing, n.Blockstore)
284
	go n.Reprovider.ProvideEvery(ctx, kReprovideFrequency)
285

286
	// setup local discovery
Jeromy's avatar
Jeromy committed
287 288 289
	if do != nil {
		service, err := do(n.PeerHost)
		if err != nil {
Jeromy's avatar
Jeromy committed
290 291 292 293
			log.Error("mdns error: ", err)
		} else {
			service.RegisterNotifee(n)
			n.Discovery = service
Jeromy's avatar
Jeromy committed
294
		}
295 296
	}

297
	return n.Bootstrap(DefaultBootstrapConfig)
298 299
}

Jeromy's avatar
Jeromy committed
300 301 302 303 304 305 306 307 308 309 310 311
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
}

312 313
func (n *IpfsNode) HandlePeerFound(p peer.PeerInfo) {
	log.Warning("trying peer info: ", p)
314
	ctx, _ := context.WithTimeout(context.TODO(), time.Second*10)
315 316 317 318 319 320
	err := n.PeerHost.Connect(ctx, p)
	if err != nil {
		log.Warning("Failed to connect to peer found by discovery: ", err)
	}
}

321 322
// startOnlineServicesWithHost  is the set of services which need to be
// initialized with the host and _before_ we start listening.
323
func (n *IpfsNode) startOnlineServicesWithHost(ctx context.Context, host p2phost.Host, routingOption RoutingOption) error {
324
	// setup diagnostics service
325
	n.Diagnostics = diag.NewDiagnostics(n.Identity, host)
326 327

	// setup routing service
328
	r, err := routingOption(ctx, host, n.Repo.Datastore())
Jeromy's avatar
Jeromy committed
329
	if err != nil {
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
330
		return err
331
	}
Jeromy's avatar
Jeromy committed
332
	n.Routing = r
333

334 335 336
	// Wrap standard peer host with routing system to allow unknown peer lookups
	n.PeerHost = rhost.Wrap(host, n.Routing)

337 338
	// setup exchange service
	const alwaysSendToPeer = true // use YesManStrategy
339
	bitswapNetwork := bsnet.NewFromIpfsHost(n.PeerHost, n.Routing)
340 341 342 343
	n.Exchange = bitswap.New(ctx, n.Identity, bitswapNetwork, n.Blockstore, alwaysSendToPeer)

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

345 346 347
	return nil
}

348 349
// teardown closes owned children. If any errors occur, this function returns
// the first error.
Brian Tiger Chow's avatar
Brian Tiger Chow committed
350
func (n *IpfsNode) teardown() error {
351
	log.Debug("core is shutting down...")
352 353
	// 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
354 355 356 357
	closers := []io.Closer{
		n.Exchange,
		n.Repo,
	}
358

Jeromy's avatar
Jeromy committed
359 360
	// Filesystem needs to be closed before network, dht, and blockservice
	// so it can use them as its shutting down
361
	if n.IpnsFs != nil {
Jeromy's avatar
Jeromy committed
362 363 364
		closers = append(closers, n.IpnsFs)
	}

Jeromy's avatar
Jeromy committed
365 366 367 368
	if n.Blocks != nil {
		closers = append(closers, n.Blocks)
	}

Jeromy's avatar
Jeromy committed
369 370
	if n.Bootstrapper != nil {
		closers = append(closers, n.Bootstrapper)
371 372
	}

373
	if dht, ok := n.Routing.(*dht.IpfsDHT); ok {
Jeromy's avatar
Jeromy committed
374 375 376 377 378
		closers = append(closers, dht)
	}

	if n.PeerHost != nil {
		closers = append(closers, n.PeerHost)
379
	}
380

381
	var errs []error
382
	for _, closer := range closers {
383 384
		if err := closer.Close(); err != nil {
			errs = append(errs, err)
385 386 387 388
		}
	}
	if len(errs) > 0 {
		return errs[0]
Brian Tiger Chow's avatar
Brian Tiger Chow committed
389 390
	}
	return nil
Brian Tiger Chow's avatar
Brian Tiger Chow committed
391 392
}

Brian Tiger Chow's avatar
Brian Tiger Chow committed
393
func (n *IpfsNode) OnlineMode() bool {
394 395 396 397 398 399
	switch n.mode {
	case onlineMode:
		return true
	default:
		return false
	}
Brian Tiger Chow's avatar
Brian Tiger Chow committed
400 401
}

402
func (n *IpfsNode) Bootstrap(cfg BootstrapConfig) error {
403 404

	// TODO what should return value be when in offlineMode?
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
405 406 407 408
	if n.Routing == nil {
		return nil
	}

409 410 411 412 413 414 415 416
	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 {
417
			ps, err := n.loadBootstrapPeers()
418
			if err != nil {
419
				log.Warningf("failed to parse bootstrap peers from config: %s", n.Repo.Config().Bootstrap)
420 421 422 423 424 425 426 427 428
				return nil
			}
			return ps
		}
	}

	var err error
	n.Bootstrapper, err = Bootstrap(n, cfg)
	return err
429 430
}

431 432
func (n *IpfsNode) loadID() error {
	if n.Identity != "" {
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
433
		return errors.New("identity already loaded")
434 435
	}

436
	cid := n.Repo.Config().Identity.PeerID
437
	if cid == "" {
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
438
		return errors.New("Identity was not set in config (was ipfs init run?)")
439 440
	}
	if len(cid) == 0 {
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
441
		return errors.New("No peer ID in config! (was ipfs init run?)")
442 443
	}

444 445 446
	n.Identity = peer.ID(b58.Decode(cid))
	return nil
}
447

Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
448
func (n *IpfsNode) LoadPrivateKey() error {
449
	if n.Identity == "" || n.Peerstore == nil {
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
450
		return errors.New("loaded private key out of order.")
451 452
	}

453
	if n.PrivateKey != nil {
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
454
		return errors.New("private key already loaded")
455 456
	}

457
	sk, err := loadPrivateKey(&n.Repo.Config().Identity, n.Identity)
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
458
	if err != nil {
459
		return err
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
460
	}
461

462 463
	n.PrivateKey = sk
	n.Peerstore.AddPrivKey(n.Identity, n.PrivateKey)
Jeromy's avatar
Jeromy committed
464 465 466 467
	n.Peerstore.AddPubKey(n.Identity, sk.GetPublic())
	return nil
}

468 469 470 471 472 473 474 475
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
476 477 478
// 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
479
func (n *IpfsNode) SetupOfflineRouting() error {
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
480
	err := n.LoadPrivateKey()
Jeromy's avatar
Jeromy committed
481 482 483 484 485
	if err != nil {
		return err
	}

	n.Routing = offroute.NewOfflineRouter(n.Repo.Datastore(), n.PrivateKey)
486 487 488

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

489
	return nil
490 491 492 493
}

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

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

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

507
	return sk, nil
508
}
509

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

	return listen, nil
}
522

Jeromy's avatar
Jeromy committed
523
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
524 525 526

var DefaultHostOption HostOption = constructPeerHost

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

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

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

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

542 543 544 545 546 547 548
	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
549
		return err
550 551
	}

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

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

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

574 575
func constructDHTRouting(ctx context.Context, host p2phost.Host, dstore ds.ThreadSafeDatastore) (routing.IpfsRouting, error) {
	dhtRouting := dht.NewDHT(ctx, host, dstore)
576
	dhtRouting.Validator[IpnsValidatorTag] = namesys.IpnsRecordValidator
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