core.go 16.7 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
	routing "github.com/ipfs/go-ipfs/routing"
	dht "github.com/ipfs/go-ipfs/routing/dht"
41
	nilrouting "github.com/ipfs/go-ipfs/routing/none"
42
	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
65
const discoveryConnTimeout = time.Second * 30
Jeromy's avatar
Jeromy committed
66

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

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

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

84
	Repo repo.Repo
85 86

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

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

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

110 111
	IpnsFs *ipnsfs.Filesystem

112 113
	proc goprocess.Process
	ctx  context.Context
114

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

214 215 216
		n.ctx = ctx
		n.proc = goprocessctx.WithContextAndTeardown(ctx, n.teardown)

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, cancel := context.WithTimeout(n.Context(), discoveryConnTimeout)
rht's avatar
rht committed
315
	defer cancel()
316
	if err := n.PeerHost.Connect(ctx, p); err != nil {
317 318 319 320
		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)
Jeromy's avatar
Jeromy committed
326
	n.Ping = ping.NewPingService(host)
327 328

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

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

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

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

346 347 348
	return nil
}

349 350 351 352 353 354 355 356 357 358 359 360
// 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 {
361 362 363
	if n.ctx == nil {
		n.ctx = context.TODO()
	}
364 365 366
	return n.ctx
}

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

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

Jeromy's avatar
Jeromy committed
391 392 393 394
	if n.Blocks != nil {
		closers = append(closers, n.Blocks)
	}

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

399
	if dht, ok := n.Routing.(*dht.IpfsDHT); ok {
400
		closers = append(closers, dht.Process())
Jeromy's avatar
Jeromy committed
401 402 403 404
	}

	if n.PeerHost != nil {
		closers = append(closers, n.PeerHost)
405
	}
406

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

Brian Tiger Chow's avatar
Brian Tiger Chow committed
419
func (n *IpfsNode) OnlineMode() bool {
420 421 422 423 424 425
	switch n.mode {
	case onlineMode:
		return true
	default:
		return false
	}
Brian Tiger Chow's avatar
Brian Tiger Chow committed
426 427
}

428
func (n *IpfsNode) Bootstrap(cfg BootstrapConfig) error {
429 430

	// TODO what should return value be when in offlineMode?
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
431 432 433 434
	if n.Routing == nil {
		return nil
	}

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

	var err error
	n.Bootstrapper, err = Bootstrap(n, cfg)
	return err
455 456
}

457 458
func (n *IpfsNode) loadID() error {
	if n.Identity != "" {
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
459
		return errors.New("identity already loaded")
460 461
	}

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

470 471 472
	n.Identity = peer.ID(b58.Decode(cid))
	return nil
}
473

Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
474
func (n *IpfsNode) LoadPrivateKey() error {
475
	if n.Identity == "" || n.Peerstore == nil {
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
476
		return errors.New("loaded private key out of order.")
477 478
	}

479
	if n.PrivateKey != nil {
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
480
		return errors.New("private key already loaded")
481 482
	}

483
	sk, err := loadPrivateKey(&n.Repo.Config().Identity, n.Identity)
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
484
	if err != nil {
485
		return err
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
486
	}
487

488 489
	n.PrivateKey = sk
	n.Peerstore.AddPrivKey(n.Identity, n.PrivateKey)
Jeromy's avatar
Jeromy committed
490 491 492 493
	n.Peerstore.AddPubKey(n.Identity, sk.GetPublic())
	return nil
}

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

	n.Routing = offroute.NewOfflineRouter(n.Repo.Datastore(), n.PrivateKey)
512 513 514

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

515
	return nil
516 517 518 519
}

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

524 525 526 527
	id2, err := peer.IDFromPrivateKey(sk)
	if err != nil {
		return nil, err
	}
528

529 530
	if id2 != id {
		return nil, fmt.Errorf("private key in config does not match id: %s != %s", id, id2)
531 532
	}

533
	return sk, nil
534
}
535

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

	return listen, nil
}
548

Jeromy's avatar
Jeromy committed
549
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
550 551 552

var DefaultHostOption HostOption = constructPeerHost

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

	// no addresses to begin with. we'll start later.
Jeromy's avatar
Jeromy committed
557
	network, err := swarm.NewNetwork(ctx, nil, id, ps, bwr)
558
	if err != nil {
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
559
		return nil, err
560
	}
561

562 563 564 565
	for _, f := range fs {
		network.Swarm().Filters.AddDialFilter(f)
	}

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

568 569 570 571 572 573 574
	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
575
		return err
576 577
	}

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

586 587 588
	// Actually start listening:
	if err := host.Network().Listen(filteredAddrs...); err != nil {
		return err
589 590
	}

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

600 601
func constructDHTRouting(ctx context.Context, host p2phost.Host, dstore ds.ThreadSafeDatastore) (routing.IpfsRouting, error) {
	dhtRouting := dht.NewDHT(ctx, host, dstore)
602
	dhtRouting.Validator[IpnsValidatorTag] = namesys.IpnsRecordValidator
603 604
	return dhtRouting, nil
}
Jeromy's avatar
Jeromy committed
605

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

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

610
var DHTOption RoutingOption = constructDHTRouting
611
var NilRouterOption RoutingOption = nilrouting.ConstructNilRouting