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

Jeromy's avatar
Jeromy committed
19
	ds "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/ipfs/go-datastore"
20
	b58 "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-base58"
21
	goprocess "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/goprocess"
Jeromy's avatar
Jeromy committed
22
	mamask "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/whyrusleeping/multiaddr-filter"
23
	diag "github.com/ipfs/go-ipfs/diagnostics"
24
	ma "gx/ipfs/QmR3JkmZBKYXgNMNsNZawm914455Qof3PEopwuVSeXG7aV/go-multiaddr"
Jeromy's avatar
Jeromy committed
25 26 27 28 29 30 31 32 33 34 35
	ic "gx/ipfs/QmZxtCsPRgCnCXwVPUjcBiFckkG5NMYM4Pthwe6X4C8uQq/go-libp2p/p2p/crypto"
	discovery "gx/ipfs/QmZxtCsPRgCnCXwVPUjcBiFckkG5NMYM4Pthwe6X4C8uQq/go-libp2p/p2p/discovery"
	p2phost "gx/ipfs/QmZxtCsPRgCnCXwVPUjcBiFckkG5NMYM4Pthwe6X4C8uQq/go-libp2p/p2p/host"
	p2pbhost "gx/ipfs/QmZxtCsPRgCnCXwVPUjcBiFckkG5NMYM4Pthwe6X4C8uQq/go-libp2p/p2p/host/basic"
	rhost "gx/ipfs/QmZxtCsPRgCnCXwVPUjcBiFckkG5NMYM4Pthwe6X4C8uQq/go-libp2p/p2p/host/routed"
	metrics "gx/ipfs/QmZxtCsPRgCnCXwVPUjcBiFckkG5NMYM4Pthwe6X4C8uQq/go-libp2p/p2p/metrics"
	swarm "gx/ipfs/QmZxtCsPRgCnCXwVPUjcBiFckkG5NMYM4Pthwe6X4C8uQq/go-libp2p/p2p/net/swarm"
	addrutil "gx/ipfs/QmZxtCsPRgCnCXwVPUjcBiFckkG5NMYM4Pthwe6X4C8uQq/go-libp2p/p2p/net/swarm/addr"
	peer "gx/ipfs/QmZxtCsPRgCnCXwVPUjcBiFckkG5NMYM4Pthwe6X4C8uQq/go-libp2p/p2p/peer"
	ping "gx/ipfs/QmZxtCsPRgCnCXwVPUjcBiFckkG5NMYM4Pthwe6X4C8uQq/go-libp2p/p2p/protocol/ping"
	context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context"
36
	logging "gx/ipfs/QmaPaGNE2GqnfJjRRpQuQuFHuJn4FZvsrGxdik4kgxCkBi/go-log"
37

38 39
	routing "github.com/ipfs/go-ipfs/routing"
	dht "github.com/ipfs/go-ipfs/routing/dht"
40
	nilrouting "github.com/ipfs/go-ipfs/routing/none"
41
	offroute "github.com/ipfs/go-ipfs/routing/offline"
42

43
	bstore "github.com/ipfs/go-ipfs/blocks/blockstore"
Jeromy's avatar
Jeromy committed
44
	key "github.com/ipfs/go-ipfs/blocks/key"
45 46 47 48 49
	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"
	rp "github.com/ipfs/go-ipfs/exchange/reprovide"
Jeromy's avatar
Jeromy committed
50
	mfs "github.com/ipfs/go-ipfs/mfs"
51

52 53 54
	mount "github.com/ipfs/go-ipfs/fuse/mount"
	merkledag "github.com/ipfs/go-ipfs/merkledag"
	namesys "github.com/ipfs/go-ipfs/namesys"
Jeromy's avatar
Jeromy committed
55
	ipnsrp "github.com/ipfs/go-ipfs/namesys/republisher"
56 57 58 59
	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"
Jeromy's avatar
Jeromy committed
60
	uio "github.com/ipfs/go-ipfs/unixfs/io"
61
	u "github.com/ipfs/go-ipfs/util"
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
62 63
)

Jeromy's avatar
Jeromy committed
64
const IpnsValidatorTag = "ipns"
65
const kSizeBlockstoreWriteCache = 100
66
const kReprovideFrequency = time.Hour * 12
67
const discoveryConnTimeout = time.Second * 30
Jeromy's avatar
Jeromy committed
68

Jeromy's avatar
Jeromy committed
69
var log = logging.Logger("core")
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
70

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

83
	// Self
84
	Identity peer.ID // the local node's identity
85

86
	Repo repo.Repo
87 88

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

	// Services
94
	Peerstore  peer.Peerstore       // storage for other Peer instances
95
	Blockstore bstore.GCBlockstore  // the block store (lower level)
96 97 98
	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
99
	Reporter   metrics.Reporter
100
	Discovery  discovery.Service
Jeromy's avatar
Jeromy committed
101
	FilesRoot  *mfs.Root
102 103

	// Online
104 105 106 107 108 109
	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
110 111
	Ping         *ping.PingService
	Reprovider   *rp.Reprovider // the value reprovider system
Jeromy's avatar
Jeromy committed
112
	IpnsRepub    *ipnsrp.Republisher
113

114 115
	proc goprocess.Process
	ctx  context.Context
116

117
	mode mode
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
118 119
}

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

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

	if n.PeerHost != nil { // already online.
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
131
		return errors.New("node already online")
132 133 134
	}

	// load private key
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
135
	if err := n.LoadPrivateKey(); err != nil {
136 137 138
		return err
	}

Jeromy's avatar
Jeromy committed
139 140 141
	// Set reporter
	n.Reporter = metrics.NewBandwidthCounter()

Jeromy's avatar
Jeromy committed
142
	// get undialable addrs from config
143 144 145 146
	cfg, err := n.Repo.Config()
	if err != nil {
		return err
	}
Jeromy's avatar
Jeromy committed
147
	var addrfilter []*net.IPNet
148
	for _, s := range cfg.Swarm.AddrFilters {
Jeromy's avatar
Jeromy committed
149 150
		f, err := mamask.NewMask(s)
		if err != nil {
151
			return fmt.Errorf("incorrectly formatted address filter in config: %s", s)
Jeromy's avatar
Jeromy committed
152 153 154 155 156
		}
		addrfilter = append(addrfilter, f)
	}

	peerhost, err := hostOption(ctx, n.Identity, n.Peerstore, n.Reporter, addrfilter)
157
	if err != nil {
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
158
		return err
159 160
	}

161
	if err := n.startOnlineServicesWithHost(ctx, peerhost, routingOption); err != nil {
162
		return err
163 164 165
	}

	// Ok, now we're ready to listen.
166
	if err := startListening(ctx, n.PeerHost, cfg); err != nil {
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
167
		return err
168
	}
169

170
	n.Reprovider = rp.NewReprovider(n.Routing, n.Blockstore)
171
	go n.Reprovider.ProvideEvery(ctx, kReprovideFrequency)
172

173
	// setup local discovery
Jeromy's avatar
Jeromy committed
174 175 176
	if do != nil {
		service, err := do(n.PeerHost)
		if err != nil {
Jeromy's avatar
Jeromy committed
177 178 179 180
			log.Error("mdns error: ", err)
		} else {
			service.RegisterNotifee(n)
			n.Discovery = service
Jeromy's avatar
Jeromy committed
181
		}
182 183
	}

184
	return n.Bootstrap(DefaultBootstrapConfig)
185 186
}

Jeromy's avatar
Jeromy committed
187 188 189 190 191 192 193 194 195 196 197 198
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
}

199 200
func (n *IpfsNode) HandlePeerFound(p peer.PeerInfo) {
	log.Warning("trying peer info: ", p)
201
	ctx, cancel := context.WithTimeout(n.Context(), discoveryConnTimeout)
rht's avatar
rht committed
202
	defer cancel()
203
	if err := n.PeerHost.Connect(ctx, p); err != nil {
204 205 206 207
		log.Warning("Failed to connect to peer found by discovery: ", err)
	}
}

208 209
// startOnlineServicesWithHost  is the set of services which need to be
// initialized with the host and _before_ we start listening.
210
func (n *IpfsNode) startOnlineServicesWithHost(ctx context.Context, host p2phost.Host, routingOption RoutingOption) error {
211
	// setup diagnostics service
212
	n.Diagnostics = diag.NewDiagnostics(n.Identity, host)
Jeromy's avatar
Jeromy committed
213
	n.Ping = ping.NewPingService(host)
214 215

	// setup routing service
216
	r, err := routingOption(ctx, host, n.Repo.Datastore())
Jeromy's avatar
Jeromy committed
217
	if err != nil {
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
218
		return err
219
	}
Jeromy's avatar
Jeromy committed
220
	n.Routing = r
221

222 223 224
	// Wrap standard peer host with routing system to allow unknown peer lookups
	n.PeerHost = rhost.Wrap(host, n.Routing)

225 226
	// setup exchange service
	const alwaysSendToPeer = true // use YesManStrategy
227
	bitswapNetwork := bsnet.NewFromIpfsHost(n.PeerHost, n.Routing)
228 229
	n.Exchange = bitswap.New(ctx, n.Identity, bitswapNetwork, n.Blockstore, alwaysSendToPeer)

230 231 232 233 234
	size, err := n.getCacheSize()
	if err != nil {
		return err
	}

235
	// setup name system
236
	n.Namesys = namesys.NewNameSystem(n.Routing, n.Repo.Datastore(), size)
237

Jeromy's avatar
Jeromy committed
238
	// setup ipns republishing
239 240 241 242
	err = n.setupIpnsRepublisher()
	if err != nil {
		return err
	}
Jeromy's avatar
Jeromy committed
243

244 245 246
	return nil
}

247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263
// getCacheSize returns cache life and cache size
func (n *IpfsNode) getCacheSize() (int, error) {
	cfg, err := n.Repo.Config()
	if err != nil {
		return 0, err
	}

	cs := cfg.Ipns.ResolveCacheSize
	if cs == 0 {
		cs = 128
	}
	if cs < 0 {
		return 0, fmt.Errorf("cannot specify negative resolve cache size")
	}
	return cs, nil
}

264
func (n *IpfsNode) setupIpnsRepublisher() error {
Jeromy's avatar
Jeromy committed
265 266 267 268
	cfg, err := n.Repo.Config()
	if err != nil {
		return err
	}
269 270 271 272

	n.IpnsRepub = ipnsrp.NewRepublisher(n.Routing, n.Repo.Datastore(), n.Peerstore)
	n.IpnsRepub.AddName(n.Identity)

Jeromy's avatar
Jeromy committed
273 274 275 276 277 278
	if cfg.Ipns.RepublishPeriod != "" {
		d, err := time.ParseDuration(cfg.Ipns.RepublishPeriod)
		if err != nil {
			return fmt.Errorf("failure to parse config setting IPNS.RepublishPeriod: %s", err)
		}

279
		if !u.Debug && (d < time.Minute || d > (time.Hour*24)) {
Jeromy's avatar
Jeromy committed
280 281 282 283 284 285
			return fmt.Errorf("config setting IPNS.RepublishPeriod is not between 1min and 1day: %s", d)
		}

		n.IpnsRepub.Interval = d
	}

286 287 288 289 290 291 292 293 294
	if cfg.Ipns.RecordLifetime != "" {
		d, err := time.ParseDuration(cfg.Ipns.RepublishPeriod)
		if err != nil {
			return fmt.Errorf("failure to parse config setting IPNS.RecordLifetime: %s", err)
		}

		n.IpnsRepub.RecordLifetime = d
	}

Jeromy's avatar
Jeromy committed
295 296
	n.Process().Go(n.IpnsRepub.Run)

297 298 299
	return nil
}

300 301 302 303 304 305 306 307 308 309 310 311
// 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 {
312 313 314
	if n.ctx == nil {
		n.ctx = context.TODO()
	}
315 316 317
	return n.ctx
}

318 319
// teardown closes owned children. If any errors occur, this function returns
// the first error.
Brian Tiger Chow's avatar
Brian Tiger Chow committed
320
func (n *IpfsNode) teardown() error {
321
	log.Debug("core is shutting down...")
322 323
	// 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
324 325 326 327 328 329 330 331
	var closers []io.Closer

	// NOTE: the order that objects are added(closed) matters, if an object
	// needs to use another during its shutdown/cleanup process, it should be
	// closed before that other object

	if n.FilesRoot != nil {
		closers = append(closers, n.FilesRoot)
Jeromy's avatar
Jeromy committed
332
	}
333

334 335 336 337
	if n.Exchange != nil {
		closers = append(closers, n.Exchange)
	}

Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
338 339 340 341 342 343 344
	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
345 346 347 348
	if dht, ok := n.Routing.(*dht.IpfsDHT); ok {
		closers = append(closers, dht.Process())
	}

Jeromy's avatar
Jeromy committed
349 350 351 352
	if n.Blocks != nil {
		closers = append(closers, n.Blocks)
	}

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

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

Jeromy's avatar
Jeromy committed
361 362 363
	// Repo closed last, most things need to preserve state here
	closers = append(closers, n.Repo)

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

Brian Tiger Chow's avatar
Brian Tiger Chow committed
376
func (n *IpfsNode) OnlineMode() bool {
377 378 379 380 381 382
	switch n.mode {
	case onlineMode:
		return true
	default:
		return false
	}
Brian Tiger Chow's avatar
Brian Tiger Chow committed
383 384
}

385
func (n *IpfsNode) Bootstrap(cfg BootstrapConfig) error {
386 387

	// TODO what should return value be when in offlineMode?
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
388 389 390 391
	if n.Routing == nil {
		return nil
	}

392 393 394 395 396 397 398 399
	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 {
400
			ps, err := n.loadBootstrapPeers()
401
			if err != nil {
402
				log.Warning("failed to parse bootstrap peers from config")
403 404 405 406 407 408 409 410 411
				return nil
			}
			return ps
		}
	}

	var err error
	n.Bootstrapper, err = Bootstrap(n, cfg)
	return err
412 413
}

414 415
func (n *IpfsNode) loadID() error {
	if n.Identity != "" {
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
416
		return errors.New("identity already loaded")
417 418
	}

419 420 421 422 423 424
	cfg, err := n.Repo.Config()
	if err != nil {
		return err
	}

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

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

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

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

445 446 447 448 449 450
	cfg, err := n.Repo.Config()
	if err != nil {
		return err
	}

	sk, err := loadPrivateKey(&cfg.Identity, n.Identity)
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
451
	if err != nil {
452
		return err
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
453
	}
454

455 456
	n.PrivateKey = sk
	n.Peerstore.AddPrivKey(n.Identity, n.PrivateKey)
Jeromy's avatar
Jeromy committed
457 458 459 460
	n.Peerstore.AddPubKey(n.Identity, sk.GetPublic())
	return nil
}

461
func (n *IpfsNode) loadBootstrapPeers() ([]peer.PeerInfo, error) {
462 463 464 465 466 467
	cfg, err := n.Repo.Config()
	if err != nil {
		return nil, err
	}

	parsed, err := cfg.BootstrapPeers()
468 469 470 471 472 473
	if err != nil {
		return nil, err
	}
	return toPeerInfos(parsed), nil
}

Jeromy's avatar
Jeromy committed
474
func (n *IpfsNode) loadFilesRoot() error {
Jeromy's avatar
Jeromy committed
475
	dsk := ds.NewKey("/local/filesroot")
Jeromy's avatar
Jeromy committed
476 477 478 479 480 481 482 483 484
	pf := func(ctx context.Context, k key.Key) error {
		return n.Repo.Datastore().Put(dsk, []byte(k))
	}

	var nd *merkledag.Node
	val, err := n.Repo.Datastore().Get(dsk)

	switch {
	case err == ds.ErrNotFound || val == nil:
Jeromy's avatar
Jeromy committed
485
		nd = uio.NewEmptyDirectory()
Jeromy's avatar
Jeromy committed
486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508
		_, err := n.DAG.Add(nd)
		if err != nil {
			return fmt.Errorf("failure writing to dagstore: %s", err)
		}
	case err == nil:
		k := key.Key(val.([]byte))
		nd, err = n.DAG.Get(n.Context(), k)
		if err != nil {
			return fmt.Errorf("error loading filesroot from DAG: %s", err)
		}
	default:
		return err
	}

	mr, err := mfs.NewRoot(n.Context(), n.DAG, nd, pf)
	if err != nil {
		return err
	}

	n.FilesRoot = mr
	return nil
}

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

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

520 521 522 523 524 525
	size, err := n.getCacheSize()
	if err != nil {
		return err
	}

	n.Namesys = namesys.NewNameSystem(n.Routing, n.Repo.Datastore(), size)
526

527
	return nil
528 529 530 531
}

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

536 537 538 539
	id2, err := peer.IDFromPrivateKey(sk)
	if err != nil {
		return nil, err
	}
540

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

545
	return sk, nil
546
}
547

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

	return listen, nil
}
560

Jeromy's avatar
Jeromy committed
561
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
562 563 564

var DefaultHostOption HostOption = constructPeerHost

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

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

574 575 576 577
	for _, f := range fs {
		network.Swarm().Filters.AddDialFilter(f)
	}

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

580 581 582 583 584 585 586
	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
587
		return err
588 589
	}

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

598 599 600
	// Actually start listening:
	if err := host.Network().Listen(filteredAddrs...); err != nil {
		return err
601 602
	}

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

Jeromy's avatar
Jeromy committed
612
func constructDHTRouting(ctx context.Context, host p2phost.Host, dstore repo.Datastore) (routing.IpfsRouting, error) {
613
	dhtRouting := dht.NewDHT(ctx, host, dstore)
614
	dhtRouting.Validator[IpnsValidatorTag] = namesys.IpnsRecordValidator
615
	dhtRouting.Selector[IpnsValidatorTag] = namesys.IpnsSelectorFunc
616 617
	return dhtRouting, nil
}
Jeromy's avatar
Jeromy committed
618

Jeromy's avatar
Jeromy committed
619
type RoutingOption func(context.Context, p2phost.Host, repo.Datastore) (routing.IpfsRouting, error)
Jeromy's avatar
Jeromy committed
620

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

623
var DHTOption RoutingOption = constructDHTRouting
624
var NilRouterOption RoutingOption = nilrouting.ConstructNilRouting