core.go 18.3 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 (
13
	"context"
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
14
	"errors"
Juan Batiz-Benet's avatar
go fmt  
Juan Batiz-Benet committed
15
	"fmt"
16
	"io"
Jeromy's avatar
Jeromy committed
17
	"net"
18
	"time"
19

20
	diag "github.com/ipfs/go-ipfs/diagnostics"
Jeromy's avatar
Jeromy committed
21

22
	goprocess "gx/ipfs/QmSF8fPo3jgVBAy8fpdjjYqgG87dkJgUprRBHRd2tmfgpP/goprocess"
Jeromy's avatar
Jeromy committed
23
	mamask "gx/ipfs/QmSMZwvs3n4GBikZ7hKzT17c3bk65FmyZo2JqtJ16swqCv/multiaddr-filter"
Jeromy's avatar
Jeromy committed
24
	logging "gx/ipfs/QmSpJByNKFX1sCsHBEp3R73FL4NF6FnQTEGyNAXHm2GS52/go-log"
Jeromy's avatar
Jeromy committed
25
	b58 "gx/ipfs/QmT8rehPR3F6bmwL6zjUN8XpiDBFFpMP2myPdC6ApsWfJf/go-base58"
Jeromy's avatar
Jeromy committed
26
	swarm "gx/ipfs/QmU9ePpXRQgGpPpMAm1CsgU9KptrtgZERrVBGB7Ek5cM2D/go-libp2p-swarm"
27
	ma "gx/ipfs/QmUAQaWbKxGCUTuoQVvvicbQNZ9APF5pDGWyAZSe93AtKH/go-multiaddr"
Jeromy's avatar
Jeromy committed
28 29 30 31
	discovery "gx/ipfs/QmUYzZRJcuUxLSnSzF1bSyw1jYbNAULkBrbS6rnr7F72uK/go-libp2p/p2p/discovery"
	p2pbhost "gx/ipfs/QmUYzZRJcuUxLSnSzF1bSyw1jYbNAULkBrbS6rnr7F72uK/go-libp2p/p2p/host/basic"
	rhost "gx/ipfs/QmUYzZRJcuUxLSnSzF1bSyw1jYbNAULkBrbS6rnr7F72uK/go-libp2p/p2p/host/routed"
	ping "gx/ipfs/QmUYzZRJcuUxLSnSzF1bSyw1jYbNAULkBrbS6rnr7F72uK/go-libp2p/p2p/protocol/ping"
32
	addrutil "gx/ipfs/QmVDnc2zvyQm8LhT72n22THcshvH7j3qPMnhvjerQER62T/go-addr-util"
Jeromy's avatar
Jeromy committed
33
	metrics "gx/ipfs/QmX4j1JhubdEt4EB1JY1mMKTvJwPZSRzTv3uwh5zaDqyAi/go-libp2p-metrics"
34
	pstore "gx/ipfs/QmXXCcQ7CLg5a81Ui9TTR35QcR4y7ZyihxwfjqaHfUVcVo/go-libp2p-peerstore"
Jeromy's avatar
Jeromy committed
35
	p2phost "gx/ipfs/Qmb6UFbVu1grhv5o5KnouvtZ6cqdrjXj6zLejAHWunxgCt/go-libp2p-host"
George Antoniadis's avatar
George Antoniadis committed
36
	ds "gx/ipfs/QmbzuUusHqaLLoNTDEVLcSF6vZDHZDLPC7p4bztRvvkXxU/go-datastore"
37
	cid "gx/ipfs/QmcEcrBAMrwMyhSjXt4yfyPpzgSuV8HLHavnfmiKCSRqZU/go-cid"
Jeromy's avatar
Jeromy committed
38
	floodsub "gx/ipfs/Qmd6gKBjErWcfJLJ22wDJu1z2EqbFKb4wheNobZJtSxf8M/floodsub"
39 40
	peer "gx/ipfs/QmfMmLGoKzCHDN7cGgk64PJr4iipzidDRME8HABSJqvmhC/go-libp2p-peer"
	ic "gx/ipfs/QmfWDLQjGjVe4fr5CoztYW2DYYjRysMJrFe1RCsXLPTf46/go-libp2p-crypto"
41

42
	nilrouting "github.com/ipfs/go-ipfs/routing/none"
43
	offroute "github.com/ipfs/go-ipfs/routing/offline"
44 45
	routing "gx/ipfs/QmUrCwTDvJgmBbJVHu1HGEyqDaod3dR6sEkZkpxZk4u47c/go-libp2p-routing"
	dht "gx/ipfs/Qmap6Qnt8RRvQ1BawQw4HZKHaSJAZC5VybyapizCXGdfUK/go-libp2p-kad-dht"
46

47 48 49 50 51 52
	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"
	rp "github.com/ipfs/go-ipfs/exchange/reprovide"
Jeromy's avatar
Jeromy committed
53
	mfs "github.com/ipfs/go-ipfs/mfs"
54

55 56 57
	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
58
	ipnsrp "github.com/ipfs/go-ipfs/namesys/republisher"
59 60 61 62
	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"
63
	ft "github.com/ipfs/go-ipfs/unixfs"
64
	u "gx/ipfs/Qmb912gdngC1UWwTkhuW8knyRbcWeu5kqkxBpveLmW8bSr/go-ipfs-util"
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
65 66
)

Jeromy's avatar
Jeromy committed
67
const IpnsValidatorTag = "ipns"
68
const kSizeBlockstoreWriteCache = 100
69
const kReprovideFrequency = time.Hour * 12
70
const discoveryConnTimeout = time.Second * 30
Jeromy's avatar
Jeromy committed
71

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

74 75 76 77 78
type mode int

const (
	// zero value is not a valid mode, must be explicitly set
	invalidMode mode = iota
79
	localMode
80 81 82 83
	offlineMode
	onlineMode
)

Juan Batiz-Benet's avatar
go lint  
Juan Batiz-Benet committed
84
// IpfsNode is IPFS Core module. It represents an IPFS instance.
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
85 86
type IpfsNode struct {

87
	// Self
88
	Identity peer.ID // the local node's identity
89

90
	Repo repo.Repo
91 92

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

	// Services
Jeromy's avatar
Jeromy committed
98
	Peerstore  pstore.Peerstore     // storage for other Peer instances
99
	Blockstore bstore.GCBlockstore  // the block store (lower level)
100
	Blocks     bserv.BlockService   // the block service, get/add blocks.
101 102
	DAG        merkledag.DAGService // the merkle dag service, get/add objects.
	Resolver   *path.Resolver       // the path resolution system
Jeromy's avatar
Jeromy committed
103
	Reporter   metrics.Reporter
104
	Discovery  discovery.Service
Jeromy's avatar
Jeromy committed
105
	FilesRoot  *mfs.Root
106 107

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

Jeromy's avatar
Jeromy committed
118 119
	Floodsub *floodsub.PubSub

120 121
	proc goprocess.Process
	ctx  context.Context
122

Jeromy's avatar
Jeromy committed
123
	mode         mode
124
	localModeSet bool
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
125 126
}

127 128 129 130 131 132 133 134
// 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
135
func (n *IpfsNode) startOnlineServices(ctx context.Context, routingOption RoutingOption, hostOption HostOption, do DiscoveryOption, pubsub bool) error {
136 137

	if n.PeerHost != nil { // already online.
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
138
		return errors.New("node already online")
139 140 141
	}

	// load private key
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
142
	if err := n.LoadPrivateKey(); err != nil {
143 144 145
		return err
	}

Jeromy's avatar
Jeromy committed
146 147 148
	// Set reporter
	n.Reporter = metrics.NewBandwidthCounter()

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

	peerhost, err := hostOption(ctx, n.Identity, n.Peerstore, n.Reporter, addrfilter)
164
	if err != nil {
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
165
		return err
166 167
	}

168
	if err := n.startOnlineServicesWithHost(ctx, peerhost, routingOption); err != nil {
169
		return err
170 171 172
	}

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

177
	n.Reprovider = rp.NewReprovider(n.Routing, n.Blockstore)
178 179 180 181 182 183 184 185 186 187 188 189 190 191

	if cfg.Reprovider.Interval != "0" {
		interval := kReprovideFrequency
		if cfg.Reprovider.Interval != "" {
			dur, err := time.ParseDuration(cfg.Reprovider.Interval)
			if err != nil {
				return err
			}

			interval = dur
		}

		go n.Reprovider.ProvideEvery(ctx, interval)
	}
192

Jeromy's avatar
Jeromy committed
193 194 195
	if pubsub {
		n.Floodsub = floodsub.NewFloodSub(ctx, peerhost)
	}
Jeromy's avatar
Jeromy committed
196

197
	// setup local discovery
Jeromy's avatar
Jeromy committed
198
	if do != nil {
Jeromy's avatar
Jeromy committed
199
		service, err := do(ctx, n.PeerHost)
Jeromy's avatar
Jeromy committed
200
		if err != nil {
Jeromy's avatar
Jeromy committed
201 202 203 204
			log.Error("mdns error: ", err)
		} else {
			service.RegisterNotifee(n)
			n.Discovery = service
Jeromy's avatar
Jeromy committed
205
		}
206 207
	}

208
	return n.Bootstrap(DefaultBootstrapConfig)
209 210
}

Jeromy's avatar
Jeromy committed
211 212
func setupDiscoveryOption(d config.Discovery) DiscoveryOption {
	if d.MDNS.Enabled {
Jeromy's avatar
Jeromy committed
213
		return func(ctx context.Context, h p2phost.Host) (discovery.Service, error) {
Jeromy's avatar
Jeromy committed
214 215 216
			if d.MDNS.Interval == 0 {
				d.MDNS.Interval = 5
			}
Jeromy's avatar
Jeromy committed
217
			return discovery.NewMdnsService(ctx, h, time.Duration(d.MDNS.Interval)*time.Second)
Jeromy's avatar
Jeromy committed
218 219 220 221 222
		}
	}
	return nil
}

Jeromy's avatar
Jeromy committed
223
func (n *IpfsNode) HandlePeerFound(p pstore.PeerInfo) {
224
	log.Warning("trying peer info: ", p)
225
	ctx, cancel := context.WithTimeout(n.Context(), discoveryConnTimeout)
rht's avatar
rht committed
226
	defer cancel()
227
	if err := n.PeerHost.Connect(ctx, p); err != nil {
228 229 230 231
		log.Warning("Failed to connect to peer found by discovery: ", err)
	}
}

232 233
// startOnlineServicesWithHost  is the set of services which need to be
// initialized with the host and _before_ we start listening.
234
func (n *IpfsNode) startOnlineServicesWithHost(ctx context.Context, host p2phost.Host, routingOption RoutingOption) error {
235
	// setup diagnostics service
236
	n.Diagnostics = diag.NewDiagnostics(n.Identity, host)
Jeromy's avatar
Jeromy committed
237
	n.Ping = ping.NewPingService(host)
238 239

	// setup routing service
240
	r, err := routingOption(ctx, host, n.Repo.Datastore())
Jeromy's avatar
Jeromy committed
241
	if err != nil {
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
242
		return err
243
	}
Jeromy's avatar
Jeromy committed
244
	n.Routing = r
245

246 247 248
	// Wrap standard peer host with routing system to allow unknown peer lookups
	n.PeerHost = rhost.Wrap(host, n.Routing)

249 250
	// setup exchange service
	const alwaysSendToPeer = true // use YesManStrategy
251
	bitswapNetwork := bsnet.NewFromIpfsHost(n.PeerHost, n.Routing)
252 253
	n.Exchange = bitswap.New(ctx, n.Identity, bitswapNetwork, n.Blockstore, alwaysSendToPeer)

254 255 256 257 258
	size, err := n.getCacheSize()
	if err != nil {
		return err
	}

259
	// setup name system
260
	n.Namesys = namesys.NewNameSystem(n.Routing, n.Repo.Datastore(), size)
261

Jeromy's avatar
Jeromy committed
262
	// setup ipns republishing
263 264 265 266
	err = n.setupIpnsRepublisher()
	if err != nil {
		return err
	}
Jeromy's avatar
Jeromy committed
267

268 269 270
	return nil
}

271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287
// 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
}

288
func (n *IpfsNode) setupIpnsRepublisher() error {
Jeromy's avatar
Jeromy committed
289 290 291 292
	cfg, err := n.Repo.Config()
	if err != nil {
		return err
	}
293 294 295 296

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

Jeromy's avatar
Jeromy committed
297 298 299 300 301 302
	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)
		}

303
		if !u.Debug && (d < time.Minute || d > (time.Hour*24)) {
Jeromy's avatar
Jeromy committed
304 305 306 307 308 309
			return fmt.Errorf("config setting IPNS.RepublishPeriod is not between 1min and 1day: %s", d)
		}

		n.IpnsRepub.Interval = d
	}

310 311 312 313 314 315 316 317 318
	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
319 320
	n.Process().Go(n.IpnsRepub.Run)

321 322 323
	return nil
}

324 325 326 327 328 329 330 331 332 333 334 335
// 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 {
336 337 338
	if n.ctx == nil {
		n.ctx = context.TODO()
	}
339 340 341
	return n.ctx
}

342 343
// teardown closes owned children. If any errors occur, this function returns
// the first error.
Brian Tiger Chow's avatar
Brian Tiger Chow committed
344
func (n *IpfsNode) teardown() error {
345
	log.Debug("core is shutting down...")
346 347
	// 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
348 349
	var closers []io.Closer

350
	// NOTE: The order that objects are added(closed) matters, if an object
Jeromy's avatar
Jeromy committed
351 352 353 354 355
	// 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
356
	}
357

358 359 360 361
	if n.Exchange != nil {
		closers = append(closers, n.Exchange)
	}

362
	if n.Mounts.Ipfs != nil && !n.Mounts.Ipfs.IsActive() {
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
363 364
		closers = append(closers, mount.Closer(n.Mounts.Ipfs))
	}
365
	if n.Mounts.Ipns != nil && !n.Mounts.Ipns.IsActive() {
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
366 367 368
		closers = append(closers, mount.Closer(n.Mounts.Ipns))
	}

Jeromy's avatar
Jeromy committed
369 370 371 372
	if dht, ok := n.Routing.(*dht.IpfsDHT); ok {
		closers = append(closers, dht.Process())
	}

Jeromy's avatar
Jeromy committed
373 374 375 376
	if n.Blocks != nil {
		closers = append(closers, n.Blocks)
	}

Jeromy's avatar
Jeromy committed
377 378
	if n.Bootstrapper != nil {
		closers = append(closers, n.Bootstrapper)
379 380
	}

Jeromy's avatar
Jeromy committed
381 382
	if n.PeerHost != nil {
		closers = append(closers, n.PeerHost)
383
	}
384

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

388
	var errs []error
389
	for _, closer := range closers {
390 391
		if err := closer.Close(); err != nil {
			errs = append(errs, err)
392 393 394 395
		}
	}
	if len(errs) > 0 {
		return errs[0]
Brian Tiger Chow's avatar
Brian Tiger Chow committed
396 397
	}
	return nil
Brian Tiger Chow's avatar
Brian Tiger Chow committed
398 399
}

Brian Tiger Chow's avatar
Brian Tiger Chow committed
400
func (n *IpfsNode) OnlineMode() bool {
401 402 403 404 405 406
	switch n.mode {
	case onlineMode:
		return true
	default:
		return false
	}
Brian Tiger Chow's avatar
Brian Tiger Chow committed
407 408
}

409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428
func (n *IpfsNode) SetLocal(isLocal bool) {
	if isLocal {
		n.mode = localMode
	}
	n.localModeSet = true
}

func (n *IpfsNode) LocalMode() bool {
	if !n.localModeSet {
		// programmer error should not happen
		panic("local mode not set")
	}
	switch n.mode {
	case localMode:
		return true
	default:
		return false
	}
}

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

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

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 {
Jeromy's avatar
Jeromy committed
443
		cfg.BootstrapPeers = func() []pstore.PeerInfo {
444
			ps, err := n.loadBootstrapPeers()
445
			if err != nil {
446
				log.Warning("failed to parse bootstrap peers from config")
447 448 449 450 451 452 453 454 455
				return nil
			}
			return ps
		}
	}

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

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

463 464 465 466 467 468
	cfg, err := n.Repo.Config()
	if err != nil {
		return err
	}

	cid := cfg.Identity.PeerID
469
	if cid == "" {
470
		return errors.New("identity was not set in config (was 'ipfs init' run?)")
471 472
	}
	if len(cid) == 0 {
473
		return errors.New("no peer ID in config! (was 'ipfs init' run?)")
474 475
	}

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

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

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

489 490 491 492 493 494
	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
495
	if err != nil {
496
		return err
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
497
	}
498

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

Jeromy's avatar
Jeromy committed
505
func (n *IpfsNode) loadBootstrapPeers() ([]pstore.PeerInfo, error) {
506 507 508 509 510 511
	cfg, err := n.Repo.Config()
	if err != nil {
		return nil, err
	}

	parsed, err := cfg.BootstrapPeers()
512 513 514 515 516 517
	if err != nil {
		return nil, err
	}
	return toPeerInfos(parsed), nil
}

Jeromy's avatar
Jeromy committed
518
func (n *IpfsNode) loadFilesRoot() error {
Jeromy's avatar
Jeromy committed
519
	dsk := ds.NewKey("/local/filesroot")
Jeromy's avatar
Jeromy committed
520 521
	pf := func(ctx context.Context, c *cid.Cid) error {
		return n.Repo.Datastore().Put(dsk, c.Bytes())
Jeromy's avatar
Jeromy committed
522 523
	}

524
	var nd *merkledag.ProtoNode
Jeromy's avatar
Jeromy committed
525 526 527 528
	val, err := n.Repo.Datastore().Get(dsk)

	switch {
	case err == ds.ErrNotFound || val == nil:
529
		nd = ft.EmptyDirNode()
Jeromy's avatar
Jeromy committed
530 531 532 533 534
		_, err := n.DAG.Add(nd)
		if err != nil {
			return fmt.Errorf("failure writing to dagstore: %s", err)
		}
	case err == nil:
Jeromy's avatar
Jeromy committed
535 536 537 538 539
		c, err := cid.Cast(val.([]byte))
		if err != nil {
			return err
		}

540
		rnd, err := n.DAG.Get(n.Context(), c)
Jeromy's avatar
Jeromy committed
541 542 543
		if err != nil {
			return fmt.Errorf("error loading filesroot from DAG: %s", err)
		}
544 545 546 547 548 549 550

		pbnd, ok := rnd.(*merkledag.ProtoNode)
		if !ok {
			return merkledag.ErrNotProtobuf
		}

		nd = pbnd
Jeromy's avatar
Jeromy committed
551 552 553 554 555 556 557 558 559 560 561 562 563
	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
564 565 566
// 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
567
func (n *IpfsNode) SetupOfflineRouting() error {
568 569 570 571
	if n.Routing != nil {
		// Routing was already set up
		return nil
	}
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
572
	err := n.LoadPrivateKey()
Jeromy's avatar
Jeromy committed
573 574 575 576 577
	if err != nil {
		return err
	}

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

579 580 581 582 583 584
	size, err := n.getCacheSize()
	if err != nil {
		return err
	}

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

586
	return nil
587 588 589 590
}

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

595 596 597 598
	id2, err := peer.IDFromPrivateKey(sk)
	if err != nil {
		return nil, err
	}
599

600 601
	if id2 != id {
		return nil, fmt.Errorf("private key in config does not match id: %s != %s", id, id2)
602 603
	}

604
	return sk, nil
605
}
606

607
func listenAddresses(cfg *config.Config) ([]ma.Multiaddr, error) {
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
608 609 610
	var listen []ma.Multiaddr
	for _, addr := range cfg.Addresses.Swarm {
		maddr, err := ma.NewMultiaddr(addr)
611
		if err != nil {
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
612
			return nil, fmt.Errorf("Failure to parse config.Addresses.Swarm: %s", cfg.Addresses.Swarm)
613
		}
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
614
		listen = append(listen, maddr)
615 616 617 618
	}

	return listen, nil
}
619

Jeromy's avatar
Jeromy committed
620
type HostOption func(ctx context.Context, id peer.ID, ps pstore.Peerstore, bwr metrics.Reporter, fs []*net.IPNet) (p2phost.Host, error)
Jeromy's avatar
Jeromy committed
621 622 623

var DefaultHostOption HostOption = constructPeerHost

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

	// no addresses to begin with. we'll start later.
Jeromy's avatar
Jeromy committed
628
	network, err := swarm.NewNetwork(ctx, nil, id, ps, bwr)
629
	if err != nil {
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
630
		return nil, err
631
	}
632

633 634 635 636
	for _, f := range fs {
		network.Swarm().Filters.AddDialFilter(f)
	}

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

639 640 641 642 643 644 645
	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
646
		return err
647 648
	}

649
	// make sure we error out if our config does not have addresses we can use
650
	log.Debugf("Config.Addresses.Swarm:%s", listenAddrs)
651
	filteredAddrs := addrutil.FilterUsableAddrs(listenAddrs)
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
652
	log.Debugf("Config.Addresses.Swarm:%s (filtered)", filteredAddrs)
653
	if len(filteredAddrs) < 1 {
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
654
		return fmt.Errorf("addresses in config not usable: %s", listenAddrs)
655 656
	}

657 658 659
	// Actually start listening:
	if err := host.Network().Listen(filteredAddrs...); err != nil {
		return err
660 661
	}

662
	// list out our addresses
663
	addrs, err := host.Network().InterfaceListenAddresses()
664
	if err != nil {
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
665
		return err
666
	}
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
667
	log.Infof("Swarm listening at: %s", addrs)
668
	return nil
669
}
670

Jeromy's avatar
Jeromy committed
671
func constructDHTRouting(ctx context.Context, host p2phost.Host, dstore repo.Datastore) (routing.IpfsRouting, error) {
672
	dhtRouting := dht.NewDHT(ctx, host, dstore)
673
	dhtRouting.Validator[IpnsValidatorTag] = namesys.IpnsRecordValidator
674
	dhtRouting.Selector[IpnsValidatorTag] = namesys.IpnsSelectorFunc
675 676
	return dhtRouting, nil
}
Jeromy's avatar
Jeromy committed
677

Jeromy's avatar
Jeromy committed
678 679 680 681 682 683 684
func constructClientDHTRouting(ctx context.Context, host p2phost.Host, dstore repo.Datastore) (routing.IpfsRouting, error) {
	dhtRouting := dht.NewDHTClient(ctx, host, dstore)
	dhtRouting.Validator[IpnsValidatorTag] = namesys.IpnsRecordValidator
	dhtRouting.Selector[IpnsValidatorTag] = namesys.IpnsSelectorFunc
	return dhtRouting, nil
}

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

Jeromy's avatar
Jeromy committed
687
type DiscoveryOption func(context.Context, p2phost.Host) (discovery.Service, error)
Jeromy's avatar
Jeromy committed
688

689
var DHTOption RoutingOption = constructDHTRouting
Jeromy's avatar
Jeromy committed
690
var DHTClientOption RoutingOption = constructClientDHTRouting
691
var NilRouterOption RoutingOption = nilrouting.ConstructNilRouting