core.go 16.9 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
	nilrouting "github.com/ipfs/go-ipfs/routing/none"
44

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

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

Jeromy's avatar
Jeromy committed
63
const IpnsValidatorTag = "ipns"
64
const kSizeBlockstoreWriteCache = 100
65
const kReprovideFrequency = time.Hour * 12
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 165 166

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

306
	return n.Bootstrap(DefaultBootstrapConfig)
307 308
}

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

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

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

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

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

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

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

355 356 357
	return nil
}

358 359 360 361 362 363 364 365 366 367 368 369
// 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 {
370 371 372
	if n.ctx == nil {
		n.ctx = context.TODO()
	}
373 374 375
	return n.ctx
}

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

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

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

Jeromy's avatar
Jeromy committed
404 405
	if n.Bootstrapper != nil {
		closers = append(closers, n.Bootstrapper)
406 407
	}

408
	if dht, ok := n.Routing.(*dht.IpfsDHT); ok {
409
		closers = append(closers, dht.Process())
Jeromy's avatar
Jeromy committed
410 411 412 413
	}

	if n.PeerHost != nil {
		closers = append(closers, n.PeerHost)
414
	}
415

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

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

437
func (n *IpfsNode) Bootstrap(cfg BootstrapConfig) error {
438 439

	// TODO what should return value be when in offlineMode?
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
440 441 442 443
	if n.Routing == nil {
		return nil
	}

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

	var err error
	n.Bootstrapper, err = Bootstrap(n, cfg)
	return err
464 465
}

466 467
func (n *IpfsNode) loadID() error {
	if n.Identity != "" {
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
468
		return errors.New("identity already loaded")
469 470
	}

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

479 480 481
	n.Identity = peer.ID(b58.Decode(cid))
	return nil
}
482

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

488
	if n.PrivateKey != nil {
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
489
		return errors.New("private key already loaded")
490 491
	}

492
	sk, err := loadPrivateKey(&n.Repo.Config().Identity, n.Identity)
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
493
	if err != nil {
494
		return err
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
495
	}
496

497 498
	n.PrivateKey = sk
	n.Peerstore.AddPrivKey(n.Identity, n.PrivateKey)
Jeromy's avatar
Jeromy committed
499 500 501 502
	n.Peerstore.AddPubKey(n.Identity, sk.GetPublic())
	return nil
}

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

	n.Routing = offroute.NewOfflineRouter(n.Repo.Datastore(), n.PrivateKey)
521 522 523

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

524
	return nil
525 526 527 528
}

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

533 534 535 536
	id2, err := peer.IDFromPrivateKey(sk)
	if err != nil {
		return nil, err
	}
537

538 539
	if id2 != id {
		return nil, fmt.Errorf("private key in config does not match id: %s != %s", id, id2)
540 541
	}

542
	return sk, nil
543
}
544

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

	return listen, nil
}
557

Jeromy's avatar
Jeromy committed
558
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
559 560 561

var DefaultHostOption HostOption = constructPeerHost

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

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

571 572 573 574
	for _, f := range fs {
		network.Swarm().Filters.AddDialFilter(f)
	}

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

577 578 579 580 581 582 583
	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
584
		return err
585 586
	}

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

595 596 597
	// Actually start listening:
	if err := host.Network().Listen(filteredAddrs...); err != nil {
		return err
598 599
	}

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

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

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

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

619
var DHTOption RoutingOption = constructDHTRouting
620
var NilRouterOption RoutingOption = nilrouting.ConstructNilRouting