core.go 16.8 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
	b58 "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-base58"
	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"
22 23
	goprocess "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/goprocess"
	goprocessctx "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/goprocess/context"
Jeromy's avatar
Jeromy committed
24
	mamask "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/whyrusleeping/multiaddr-filter"
25 26
	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
27
	metrics "github.com/ipfs/go-ipfs/metrics"
28
	ic "github.com/ipfs/go-ipfs/p2p/crypto"
29
	discovery "github.com/ipfs/go-ipfs/p2p/discovery"
30 31 32 33 34 35
	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
36
	ping "github.com/ipfs/go-ipfs/p2p/protocol/ping"
Jeromy's avatar
Jeromy committed
37
	eventlog "github.com/ipfs/go-ipfs/thirdparty/eventlog"
38

39 40 41 42
	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"
43

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

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

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

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

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

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

83
	Repo repo.Repo
84 85

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

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

	// Online
100 101 102 103 104 105
	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
106 107
	Ping         *ping.PingService
	Reprovider   *rp.Reprovider // the value reprovider system
108

109 110
	IpnsFs *ipnsfs.Filesystem

111 112
	proc goprocess.Process
	ctx  context.Context
113

114
	mode mode
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
115 116
}

117 118 119 120 121 122 123 124
// 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
}

125
type ConfigOption func(ctx context.Context) (*IpfsNode, error)
126

127 128 129 130 131 132
func NewIPFSNode(ctx context.Context, option ConfigOption) (*IpfsNode, error) {
	node, err := option(ctx)
	if err != nil {
		return nil, err
	}

133 134 135 136 137 138
	if node.ctx == nil {
		node.ctx = ctx
	}
	if node.proc == nil {
		node.proc = goprocessctx.WithContextAndTeardown(node.ctx, node.teardown)
	}
139

140 141 142
	success := false // flip to true after all sub-system inits succeed
	defer func() {
		if !success {
143
			node.proc.Close()
144 145 146
		}
	}()

147 148 149 150
	// 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.

151 152
	node.Blocks = bserv.New(node.Blockstore, node.Exchange)

153 154 155
	if node.Peerstore == nil {
		node.Peerstore = peer.NewPeerstore()
	}
156
	node.DAG = merkledag.NewDAGService(node.Blocks)
157
	node.Pinning, err = pin.LoadPinner(node.Repo.Datastore(), node.DAG)
158
	if err != nil {
159
		node.Pinning = pin.NewPinner(node.Repo.Datastore(), node.DAG)
160 161
	}
	node.Resolver = &path.Resolver{DAG: node.DAG}
162 163 164 165

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

172
	success = true
Brian Tiger Chow's avatar
Brian Tiger Chow committed
173
	return node, nil
174 175
}

176 177
func Offline(r repo.Repo) ConfigOption {
	return Standard(r, false)
178 179
}

Jeromy's avatar
Jeromy committed
180 181
func OnlineWithOptions(r repo.Repo, router RoutingOption, ho HostOption) ConfigOption {
	return standardWithRouting(r, true, router, ho)
182 183
}

184 185
func Online(r repo.Repo) ConfigOption {
	return Standard(r, true)
186 187 188
}

// DEPRECATED: use Online, Offline functions
189
func Standard(r repo.Repo, online bool) ConfigOption {
Jeromy's avatar
Jeromy committed
190
	return standardWithRouting(r, online, DHTOption, DefaultHostOption)
191 192 193
}

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

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

222 223 224
		n.ctx = ctx
		n.proc = goprocessctx.WithContextAndTeardown(ctx, n.teardown)

225 226
		// setup Peerstore
		n.Peerstore = peer.NewPeerstore()
227

228 229 230 231
		// 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
232

233
		n.Blockstore, err = bstore.WriteCached(bstore.NewBlockstore(n.Repo.Datastore()), kSizeBlockstoreWriteCache)
234
		if err != nil {
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
235
			return nil, err
236
		}
237

238
		if online {
Jeromy's avatar
Jeromy committed
239 240
			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
241
				return nil, err
242 243 244 245
			}
		} else {
			n.Exchange = offline.Exchange(n.Blockstore)
		}
246

Brian Tiger Chow's avatar
Brian Tiger Chow committed
247
		success = true
248
		return n, nil
Jeromy's avatar
Jeromy committed
249
	}
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
250
}
251

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

	if n.PeerHost != nil { // already online.
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
255
		return errors.New("node already online")
256 257 258
	}

	// load private key
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
259
	if err := n.LoadPrivateKey(); err != nil {
260 261 262
		return err
	}

Jeromy's avatar
Jeromy committed
263 264 265
	// Set reporter
	n.Reporter = metrics.NewBandwidthCounter()

Jeromy's avatar
Jeromy committed
266 267 268
	// get undialable addrs from config
	cfg := n.Repo.Config()
	var addrfilter []*net.IPNet
269
	for _, s := range cfg.Swarm.AddrFilters {
Jeromy's avatar
Jeromy committed
270 271 272 273 274 275 276 277
		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)
278
	if err != nil {
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
279
		return err
280 281
	}

282
	if err := n.startOnlineServicesWithHost(ctx, peerhost, routingOption); err != nil {
283
		return err
284 285 286 287
	}

	// 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
288
		return err
289
	}
290

291
	n.Reprovider = rp.NewReprovider(n.Routing, n.Blockstore)
292
	go n.Reprovider.ProvideEvery(ctx, kReprovideFrequency)
293

294
	// setup local discovery
Jeromy's avatar
Jeromy committed
295 296 297
	if do != nil {
		service, err := do(n.PeerHost)
		if err != nil {
Jeromy's avatar
Jeromy committed
298 299 300 301
			log.Error("mdns error: ", err)
		} else {
			service.RegisterNotifee(n)
			n.Discovery = service
Jeromy's avatar
Jeromy committed
302
		}
303 304
	}

305
	return n.Bootstrap(DefaultBootstrapConfig)
306 307
}

Jeromy's avatar
Jeromy committed
308 309 310 311 312 313 314 315 316 317 318 319
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
}

320 321
func (n *IpfsNode) HandlePeerFound(p peer.PeerInfo) {
	log.Warning("trying peer info: ", p)
322
	ctx, _ := context.WithTimeout(context.TODO(), time.Second*10)
323 324 325 326 327 328
	err := n.PeerHost.Connect(ctx, p)
	if err != nil {
		log.Warning("Failed to connect to peer found by discovery: ", err)
	}
}

329 330
// startOnlineServicesWithHost  is the set of services which need to be
// initialized with the host and _before_ we start listening.
331
func (n *IpfsNode) startOnlineServicesWithHost(ctx context.Context, host p2phost.Host, routingOption RoutingOption) error {
332
	// setup diagnostics service
333
	n.Diagnostics = diag.NewDiagnostics(n.Identity, host)
Jeromy's avatar
Jeromy committed
334
	n.Ping = ping.NewPingService(host)
335 336

	// setup routing service
337
	r, err := routingOption(ctx, host, n.Repo.Datastore())
Jeromy's avatar
Jeromy committed
338
	if err != nil {
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
339
		return err
340
	}
Jeromy's avatar
Jeromy committed
341
	n.Routing = r
342

343 344 345
	// Wrap standard peer host with routing system to allow unknown peer lookups
	n.PeerHost = rhost.Wrap(host, n.Routing)

346 347
	// setup exchange service
	const alwaysSendToPeer = true // use YesManStrategy
348
	bitswapNetwork := bsnet.NewFromIpfsHost(n.PeerHost, n.Routing)
349 350 351 352
	n.Exchange = bitswap.New(ctx, n.Identity, bitswapNetwork, n.Blockstore, alwaysSendToPeer)

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

354 355 356
	return nil
}

357 358 359 360 361 362 363 364 365 366 367 368 369 370 371
// 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 {
	return n.ctx
}

372 373
// teardown closes owned children. If any errors occur, this function returns
// the first error.
Brian Tiger Chow's avatar
Brian Tiger Chow committed
374
func (n *IpfsNode) teardown() error {
375
	log.Debug("core is shutting down...")
376 377
	// 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
378 379 380 381
	closers := []io.Closer{
		n.Exchange,
		n.Repo,
	}
382

Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
383 384 385 386 387 388 389
	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
390 391
	// Filesystem needs to be closed before network, dht, and blockservice
	// so it can use them as its shutting down
392
	if n.IpnsFs != nil {
Jeromy's avatar
Jeromy committed
393 394 395
		closers = append(closers, n.IpnsFs)
	}

Jeromy's avatar
Jeromy committed
396 397 398 399
	if n.Blocks != nil {
		closers = append(closers, n.Blocks)
	}

Jeromy's avatar
Jeromy committed
400 401
	if n.Bootstrapper != nil {
		closers = append(closers, n.Bootstrapper)
402 403
	}

404
	if dht, ok := n.Routing.(*dht.IpfsDHT); ok {
405
		closers = append(closers, dht.Process())
Jeromy's avatar
Jeromy committed
406 407 408 409
	}

	if n.PeerHost != nil {
		closers = append(closers, n.PeerHost)
410
	}
411

412
	var errs []error
413
	for _, closer := range closers {
414 415
		if err := closer.Close(); err != nil {
			errs = append(errs, err)
416 417 418 419
		}
	}
	if len(errs) > 0 {
		return errs[0]
Brian Tiger Chow's avatar
Brian Tiger Chow committed
420 421
	}
	return nil
Brian Tiger Chow's avatar
Brian Tiger Chow committed
422 423
}

Brian Tiger Chow's avatar
Brian Tiger Chow committed
424
func (n *IpfsNode) OnlineMode() bool {
425 426 427 428 429 430
	switch n.mode {
	case onlineMode:
		return true
	default:
		return false
	}
Brian Tiger Chow's avatar
Brian Tiger Chow committed
431 432
}

433
func (n *IpfsNode) Bootstrap(cfg BootstrapConfig) error {
434 435

	// TODO what should return value be when in offlineMode?
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
436 437 438 439
	if n.Routing == nil {
		return nil
	}

440 441 442 443 444 445 446 447
	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 {
448
			ps, err := n.loadBootstrapPeers()
449
			if err != nil {
450
				log.Warningf("failed to parse bootstrap peers from config: %s", n.Repo.Config().Bootstrap)
451 452 453 454 455 456 457 458 459
				return nil
			}
			return ps
		}
	}

	var err error
	n.Bootstrapper, err = Bootstrap(n, cfg)
	return err
460 461
}

462 463
func (n *IpfsNode) loadID() error {
	if n.Identity != "" {
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
464
		return errors.New("identity already loaded")
465 466
	}

467
	cid := n.Repo.Config().Identity.PeerID
468
	if cid == "" {
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
469
		return errors.New("Identity was not set in config (was ipfs init run?)")
470 471
	}
	if len(cid) == 0 {
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
472
		return errors.New("No peer ID in config! (was ipfs init run?)")
473 474
	}

475 476 477
	n.Identity = peer.ID(b58.Decode(cid))
	return nil
}
478

Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
479
func (n *IpfsNode) LoadPrivateKey() error {
480
	if n.Identity == "" || n.Peerstore == nil {
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
481
		return errors.New("loaded private key out of order.")
482 483
	}

484
	if n.PrivateKey != nil {
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
485
		return errors.New("private key already loaded")
486 487
	}

488
	sk, err := loadPrivateKey(&n.Repo.Config().Identity, n.Identity)
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
489
	if err != nil {
490
		return err
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
491
	}
492

493 494
	n.PrivateKey = sk
	n.Peerstore.AddPrivKey(n.Identity, n.PrivateKey)
Jeromy's avatar
Jeromy committed
495 496 497 498
	n.Peerstore.AddPubKey(n.Identity, sk.GetPublic())
	return nil
}

499 500 501 502 503 504 505 506
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
507 508 509
// 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
510
func (n *IpfsNode) SetupOfflineRouting() error {
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
511
	err := n.LoadPrivateKey()
Jeromy's avatar
Jeromy committed
512 513 514 515 516
	if err != nil {
		return err
	}

	n.Routing = offroute.NewOfflineRouter(n.Repo.Datastore(), n.PrivateKey)
517 518 519

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

520
	return nil
521 522 523 524
}

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

529 530 531 532
	id2, err := peer.IDFromPrivateKey(sk)
	if err != nil {
		return nil, err
	}
533

534 535
	if id2 != id {
		return nil, fmt.Errorf("private key in config does not match id: %s != %s", id, id2)
536 537
	}

538
	return sk, nil
539
}
540

541
func listenAddresses(cfg *config.Config) ([]ma.Multiaddr, error) {
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
542 543 544
	var listen []ma.Multiaddr
	for _, addr := range cfg.Addresses.Swarm {
		maddr, err := ma.NewMultiaddr(addr)
545
		if err != nil {
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
546
			return nil, fmt.Errorf("Failure to parse config.Addresses.Swarm: %s", cfg.Addresses.Swarm)
547
		}
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
548
		listen = append(listen, maddr)
549 550 551 552
	}

	return listen, nil
}
553

Jeromy's avatar
Jeromy committed
554
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
555 556 557

var DefaultHostOption HostOption = constructPeerHost

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

	// no addresses to begin with. we'll start later.
Jeromy's avatar
Jeromy committed
562
	network, err := swarm.NewNetwork(ctx, nil, id, ps, bwr)
563
	if err != nil {
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
564
		return nil, err
565
	}
566

567 568 569 570
	for _, f := range fs {
		network.Swarm().Filters.AddDialFilter(f)
	}

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

573 574 575 576 577 578 579
	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
580
		return err
581 582
	}

583
	// make sure we error out if our config does not have addresses we can use
584
	log.Debugf("Config.Addresses.Swarm:%s", listenAddrs)
585
	filteredAddrs := addrutil.FilterUsableAddrs(listenAddrs)
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
586
	log.Debugf("Config.Addresses.Swarm:%s (filtered)", filteredAddrs)
587
	if len(filteredAddrs) < 1 {
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
588
		return fmt.Errorf("addresses in config not usable: %s", listenAddrs)
589 590
	}

591 592 593
	// Actually start listening:
	if err := host.Network().Listen(filteredAddrs...); err != nil {
		return err
594 595
	}

596
	// list out our addresses
597
	addrs, err := host.Network().InterfaceListenAddresses()
598
	if err != nil {
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
599
		return err
600
	}
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
601
	log.Infof("Swarm listening at: %s", addrs)
602
	return nil
603
}
604

605 606
func constructDHTRouting(ctx context.Context, host p2phost.Host, dstore ds.ThreadSafeDatastore) (routing.IpfsRouting, error) {
	dhtRouting := dht.NewDHT(ctx, host, dstore)
607
	dhtRouting.Validator[IpnsValidatorTag] = namesys.IpnsRecordValidator
608 609
	return dhtRouting, nil
}
Jeromy's avatar
Jeromy committed
610

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

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

615
var DHTOption RoutingOption = constructDHTRouting