core.go 20 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"
17
	"io/ioutil"
Jeromy's avatar
Jeromy committed
18
	"net"
19 20
	"os"
	"strings"
21
	"time"
22

23 24
	bstore "github.com/ipfs/go-ipfs/blocks/blockstore"
	bserv "github.com/ipfs/go-ipfs/blockservice"
25
	diag "github.com/ipfs/go-ipfs/diagnostics"
26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41
	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"
	mount "github.com/ipfs/go-ipfs/fuse/mount"
	merkledag "github.com/ipfs/go-ipfs/merkledag"
	mfs "github.com/ipfs/go-ipfs/mfs"
	namesys "github.com/ipfs/go-ipfs/namesys"
	ipnsrp "github.com/ipfs/go-ipfs/namesys/republisher"
	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"
	nilrouting "github.com/ipfs/go-ipfs/routing/none"
	offroute "github.com/ipfs/go-ipfs/routing/offline"
	ft "github.com/ipfs/go-ipfs/unixfs"
Jeromy's avatar
Jeromy committed
42

43 44
	p2phost "gx/ipfs/QmPsRtodRuBUir32nz5v4zuSBTSszrR1d3fA6Ahb6eaejj/go-libp2p-host"
	mssmux "gx/ipfs/QmRVYfZ7tWNHPBzWiG6KWGzvT2hcGems8srihsQE29x1U5/go-smux-multistream"
Jeromy's avatar
Jeromy committed
45
	ds "gx/ipfs/QmRWDav6mzWseLWeYfVd5fvUKiVe9xNH29YfMF438fG364/go-datastore"
46
	goprocess "gx/ipfs/QmSF8fPo3jgVBAy8fpdjjYqgG87dkJgUprRBHRd2tmfgpP/goprocess"
Jeromy's avatar
Jeromy committed
47
	mamask "gx/ipfs/QmSMZwvs3n4GBikZ7hKzT17c3bk65FmyZo2JqtJ16swqCv/multiaddr-filter"
Jeromy's avatar
Jeromy committed
48
	logging "gx/ipfs/QmSpJByNKFX1sCsHBEp3R73FL4NF6FnQTEGyNAXHm2GS52/go-log"
Jeromy's avatar
Jeromy committed
49
	b58 "gx/ipfs/QmT8rehPR3F6bmwL6zjUN8XpiDBFFpMP2myPdC6ApsWfJf/go-base58"
50 51
	ma "gx/ipfs/QmUAQaWbKxGCUTuoQVvvicbQNZ9APF5pDGWyAZSe93AtKH/go-multiaddr"
	addrutil "gx/ipfs/QmVDnc2zvyQm8LhT72n22THcshvH7j3qPMnhvjerQER62T/go-addr-util"
52
	spdy "gx/ipfs/QmWUNsat6Jb19nC5CiJCDXepTkxjdxi3eZqeoB6mrmmaGu/go-smux-spdystream"
53
	floodsub "gx/ipfs/QmX3L7GWQ2HfvdwUM7kFJrtKfycamfhricAFSshyHtCB3h/floodsub"
Jeromy's avatar
Jeromy committed
54
	metrics "gx/ipfs/QmY2otvyPM2sTaDsczo7Yuosg98sUMCJ9qx1gpPaAPTS9B/go-libp2p-metrics"
55
	dht "gx/ipfs/QmZbinR1CdVPaoom5vgD5YC5c1oeCPJqYhoGJFXoA32GKn/go-libp2p-kad-dht"
56
	u "gx/ipfs/Qmb912gdngC1UWwTkhuW8knyRbcWeu5kqkxBpveLmW8bSr/go-ipfs-util"
57
	mplex "gx/ipfs/QmbPSoTDH3JFKkSxsCQDyVMFT76KcQz7ELWEetyyg3aeDA/go-smux-multiplex"
58
	swarm "gx/ipfs/Qmbiq2d2ZMi34A6V22kNY3b4GgPGFztmRCQZ931TJkYWp7/go-libp2p-swarm"
Jeromy's avatar
Jeromy committed
59
	routing "gx/ipfs/QmbkGVaN9W6RYJK4Ws5FvMKXKDqdRQ5snhtaa92qP6L8eU/go-libp2p-routing"
60
	yamux "gx/ipfs/Qmbn7RYyWzBVXiUp9jZ1dA4VADHy9DtS7iZLwfhEUQvm3U/go-smux-yamux"
Jeromy's avatar
Jeromy committed
61
	cid "gx/ipfs/QmcTcsTvfaeEBRFo1TkFgT8sRmgi1n1LTZpecfVP8fzpGD/go-cid"
62 63 64 65 66
	discovery "gx/ipfs/QmdzDdLZ7nj133QvNHypyS9Y39g35bMFk5DJ2pmX7YqtKU/go-libp2p/p2p/discovery"
	p2pbhost "gx/ipfs/QmdzDdLZ7nj133QvNHypyS9Y39g35bMFk5DJ2pmX7YqtKU/go-libp2p/p2p/host/basic"
	rhost "gx/ipfs/QmdzDdLZ7nj133QvNHypyS9Y39g35bMFk5DJ2pmX7YqtKU/go-libp2p/p2p/host/routed"
	identify "gx/ipfs/QmdzDdLZ7nj133QvNHypyS9Y39g35bMFk5DJ2pmX7YqtKU/go-libp2p/p2p/protocol/identify"
	ping "gx/ipfs/QmdzDdLZ7nj133QvNHypyS9Y39g35bMFk5DJ2pmX7YqtKU/go-libp2p/p2p/protocol/ping"
Jeromy's avatar
Jeromy committed
67
	pstore "gx/ipfs/QmeXj9VAjmYQZxpmVz7VzccbJrpmr8qkCDSjfVNsPTWTYU/go-libp2p-peerstore"
68
	smux "gx/ipfs/QmeZBgYBHvxMukGK5ojg28BCNLB9SeXqT7XXg6o7r2GbJy/go-stream-muxer"
69 70
	peer "gx/ipfs/QmfMmLGoKzCHDN7cGgk64PJr4iipzidDRME8HABSJqvmhC/go-libp2p-peer"
	ic "gx/ipfs/QmfWDLQjGjVe4fr5CoztYW2DYYjRysMJrFe1RCsXLPTf46/go-libp2p-crypto"
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
71 72
)

Jeromy's avatar
Jeromy committed
73
const IpnsValidatorTag = "ipns"
74
const kSizeBlockstoreWriteCache = 100
75
const kReprovideFrequency = time.Hour * 12
76
const discoveryConnTimeout = time.Second * 30
Jeromy's avatar
Jeromy committed
77

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

80 81 82 83 84
type mode int

const (
	// zero value is not a valid mode, must be explicitly set
	invalidMode mode = iota
85
	localMode
86 87 88 89
	offlineMode
	onlineMode
)

90 91 92 93
func init() {
	identify.ClientVersion = "go-ipfs/" + config.CurrentVersionNumber + "/" + config.CurrentCommit
}

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

97
	// Self
98
	Identity peer.ID // the local node's identity
99

100
	Repo repo.Repo
101 102

	// Local node
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
103 104 105
	Pinning    pin.Pinner // the pinning manager
	Mounts     Mounts     // current mount state, if any.
	PrivateKey ic.PrivKey // the local node's private Key
106 107

	// Services
Jeromy's avatar
Jeromy committed
108
	Peerstore  pstore.Peerstore     // storage for other Peer instances
109
	Blockstore bstore.GCBlockstore  // the block store (lower level)
110
	Blocks     bserv.BlockService   // the block service, get/add blocks.
111 112
	DAG        merkledag.DAGService // the merkle dag service, get/add objects.
	Resolver   *path.Resolver       // the path resolution system
Jeromy's avatar
Jeromy committed
113
	Reporter   metrics.Reporter
114
	Discovery  discovery.Service
Jeromy's avatar
Jeromy committed
115
	FilesRoot  *mfs.Root
116 117

	// Online
118 119 120 121 122 123
	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
124 125
	Ping         *ping.PingService
	Reprovider   *rp.Reprovider // the value reprovider system
Jeromy's avatar
Jeromy committed
126
	IpnsRepub    *ipnsrp.Republisher
127

Jeromy's avatar
Jeromy committed
128 129
	Floodsub *floodsub.PubSub

130 131
	proc goprocess.Process
	ctx  context.Context
132

Jeromy's avatar
Jeromy committed
133
	mode         mode
134
	localModeSet bool
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
135 136
}

137 138 139 140 141 142 143 144
// 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
}

145
func (n *IpfsNode) startOnlineServices(ctx context.Context, routingOption RoutingOption, hostOption HostOption, do DiscoveryOption, pubsub, mplex bool) error {
146 147

	if n.PeerHost != nil { // already online.
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
148
		return errors.New("node already online")
149 150 151
	}

	// load private key
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
152
	if err := n.LoadPrivateKey(); err != nil {
153 154 155
		return err
	}

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

170 171 172 173 174
	if !cfg.Swarm.DisableBandwidthMetrics {
		// Set reporter
		n.Reporter = metrics.NewBandwidthCounter()
	}

175 176 177
	tpt := makeSmuxTransport(mplex)

	peerhost, err := hostOption(ctx, n.Identity, n.Peerstore, n.Reporter, addrfilter, tpt)
178
	if err != nil {
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
179
		return err
180 181
	}

182
	if err := n.startOnlineServicesWithHost(ctx, peerhost, routingOption); err != nil {
183
		return err
184 185 186
	}

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

191
	n.Reprovider = rp.NewReprovider(n.Routing, n.Blockstore)
192 193 194 195 196 197 198 199 200 201 202 203 204 205

	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)
	}
206

Jeromy's avatar
Jeromy committed
207 208 209
	if pubsub {
		n.Floodsub = floodsub.NewFloodSub(ctx, peerhost)
	}
Jeromy's avatar
Jeromy committed
210

211
	// setup local discovery
Jeromy's avatar
Jeromy committed
212
	if do != nil {
Jeromy's avatar
Jeromy committed
213
		service, err := do(ctx, n.PeerHost)
Jeromy's avatar
Jeromy committed
214
		if err != nil {
Jeromy's avatar
Jeromy committed
215 216 217 218
			log.Error("mdns error: ", err)
		} else {
			service.RegisterNotifee(n)
			n.Discovery = service
Jeromy's avatar
Jeromy committed
219
		}
220 221
	}

222
	return n.Bootstrap(DefaultBootstrapConfig)
223 224
}

225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252
func makeSmuxTransport(mplexExp bool) smux.Transport {
	mstpt := mssmux.NewBlankTransport()

	ymxtpt := &yamux.Transport{
		AcceptBacklog:          8192,
		ConnectionWriteTimeout: time.Second * 10,
		KeepAliveInterval:      time.Second * 30,
		EnableKeepAlive:        true,
		MaxStreamWindowSize:    uint32(1024 * 512),
		LogOutput:              ioutil.Discard,
	}

	mstpt.AddTransport("/yamux/1.0.0", ymxtpt)

	mstpt.AddTransport("/spdy/3.1.0", spdy.Transport)

	if mplexExp {
		mstpt.AddTransport("/mplex/6.7.0", mplex.DefaultTransport)
	}

	// Allow muxer preference order overriding
	if prefs := os.Getenv("LIBP2P_MUX_PREFS"); prefs != "" {
		mstpt.OrderPreference = strings.Fields(prefs)
	}

	return mstpt
}

Jeromy's avatar
Jeromy committed
253 254
func setupDiscoveryOption(d config.Discovery) DiscoveryOption {
	if d.MDNS.Enabled {
Jeromy's avatar
Jeromy committed
255
		return func(ctx context.Context, h p2phost.Host) (discovery.Service, error) {
Jeromy's avatar
Jeromy committed
256 257 258
			if d.MDNS.Interval == 0 {
				d.MDNS.Interval = 5
			}
Jeromy's avatar
Jeromy committed
259
			return discovery.NewMdnsService(ctx, h, time.Duration(d.MDNS.Interval)*time.Second)
Jeromy's avatar
Jeromy committed
260 261 262 263 264
		}
	}
	return nil
}

Jeromy's avatar
Jeromy committed
265
func (n *IpfsNode) HandlePeerFound(p pstore.PeerInfo) {
266
	log.Warning("trying peer info: ", p)
267
	ctx, cancel := context.WithTimeout(n.Context(), discoveryConnTimeout)
rht's avatar
rht committed
268
	defer cancel()
269
	if err := n.PeerHost.Connect(ctx, p); err != nil {
270 271 272 273
		log.Warning("Failed to connect to peer found by discovery: ", err)
	}
}

274 275
// startOnlineServicesWithHost  is the set of services which need to be
// initialized with the host and _before_ we start listening.
276
func (n *IpfsNode) startOnlineServicesWithHost(ctx context.Context, host p2phost.Host, routingOption RoutingOption) error {
277
	// setup diagnostics service
278
	n.Diagnostics = diag.NewDiagnostics(n.Identity, host)
Jeromy's avatar
Jeromy committed
279
	n.Ping = ping.NewPingService(host)
280 281

	// setup routing service
282
	r, err := routingOption(ctx, host, n.Repo.Datastore())
Jeromy's avatar
Jeromy committed
283
	if err != nil {
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
284
		return err
285
	}
Jeromy's avatar
Jeromy committed
286
	n.Routing = r
287

288 289 290
	// Wrap standard peer host with routing system to allow unknown peer lookups
	n.PeerHost = rhost.Wrap(host, n.Routing)

291 292
	// setup exchange service
	const alwaysSendToPeer = true // use YesManStrategy
293
	bitswapNetwork := bsnet.NewFromIpfsHost(n.PeerHost, n.Routing)
294 295
	n.Exchange = bitswap.New(ctx, n.Identity, bitswapNetwork, n.Blockstore, alwaysSendToPeer)

296 297 298 299 300
	size, err := n.getCacheSize()
	if err != nil {
		return err
	}

301
	// setup name system
302
	n.Namesys = namesys.NewNameSystem(n.Routing, n.Repo.Datastore(), size)
303

Jeromy's avatar
Jeromy committed
304
	// setup ipns republishing
305 306 307 308
	err = n.setupIpnsRepublisher()
	if err != nil {
		return err
	}
Jeromy's avatar
Jeromy committed
309

310 311 312
	return nil
}

313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329
// 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
}

330
func (n *IpfsNode) setupIpnsRepublisher() error {
Jeromy's avatar
Jeromy committed
331 332 333 334
	cfg, err := n.Repo.Config()
	if err != nil {
		return err
	}
335 336 337 338

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

Jeromy's avatar
Jeromy committed
339 340 341 342 343 344
	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)
		}

345
		if !u.Debug && (d < time.Minute || d > (time.Hour*24)) {
Jeromy's avatar
Jeromy committed
346 347 348 349 350 351
			return fmt.Errorf("config setting IPNS.RepublishPeriod is not between 1min and 1day: %s", d)
		}

		n.IpnsRepub.Interval = d
	}

352 353 354 355 356 357 358 359 360
	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
361 362
	n.Process().Go(n.IpnsRepub.Run)

363 364 365
	return nil
}

366 367 368 369 370 371 372 373 374 375 376 377
// 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 {
378 379 380
	if n.ctx == nil {
		n.ctx = context.TODO()
	}
381 382 383
	return n.ctx
}

384 385
// teardown closes owned children. If any errors occur, this function returns
// the first error.
Brian Tiger Chow's avatar
Brian Tiger Chow committed
386
func (n *IpfsNode) teardown() error {
387
	log.Debug("core is shutting down...")
388 389
	// 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
390 391
	var closers []io.Closer

392
	// NOTE: The order that objects are added(closed) matters, if an object
Jeromy's avatar
Jeromy committed
393 394 395 396 397
	// 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
398
	}
399

400 401 402 403
	if n.Exchange != nil {
		closers = append(closers, n.Exchange)
	}

404
	if n.Mounts.Ipfs != nil && !n.Mounts.Ipfs.IsActive() {
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
405 406
		closers = append(closers, mount.Closer(n.Mounts.Ipfs))
	}
407
	if n.Mounts.Ipns != nil && !n.Mounts.Ipns.IsActive() {
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
408 409 410
		closers = append(closers, mount.Closer(n.Mounts.Ipns))
	}

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

Jeromy's avatar
Jeromy committed
415 416 417 418
	if n.Blocks != nil {
		closers = append(closers, n.Blocks)
	}

Jeromy's avatar
Jeromy committed
419 420
	if n.Bootstrapper != nil {
		closers = append(closers, n.Bootstrapper)
421 422
	}

Jeromy's avatar
Jeromy committed
423 424
	if n.PeerHost != nil {
		closers = append(closers, n.PeerHost)
425
	}
426

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

430
	var errs []error
431
	for _, closer := range closers {
432 433
		if err := closer.Close(); err != nil {
			errs = append(errs, err)
434 435 436 437
		}
	}
	if len(errs) > 0 {
		return errs[0]
Brian Tiger Chow's avatar
Brian Tiger Chow committed
438 439
	}
	return nil
Brian Tiger Chow's avatar
Brian Tiger Chow committed
440 441
}

Brian Tiger Chow's avatar
Brian Tiger Chow committed
442
func (n *IpfsNode) OnlineMode() bool {
443 444 445 446 447 448
	switch n.mode {
	case onlineMode:
		return true
	default:
		return false
	}
Brian Tiger Chow's avatar
Brian Tiger Chow committed
449 450
}

451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470
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
	}
}

471
func (n *IpfsNode) Bootstrap(cfg BootstrapConfig) error {
472 473

	// TODO what should return value be when in offlineMode?
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
474 475 476 477
	if n.Routing == nil {
		return nil
	}

478 479 480 481 482 483 484
	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
485
		cfg.BootstrapPeers = func() []pstore.PeerInfo {
486
			ps, err := n.loadBootstrapPeers()
487
			if err != nil {
488
				log.Warning("failed to parse bootstrap peers from config")
489 490 491 492 493 494 495 496 497
				return nil
			}
			return ps
		}
	}

	var err error
	n.Bootstrapper, err = Bootstrap(n, cfg)
	return err
498 499
}

500 501
func (n *IpfsNode) loadID() error {
	if n.Identity != "" {
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
502
		return errors.New("identity already loaded")
503 504
	}

505 506 507 508 509 510
	cfg, err := n.Repo.Config()
	if err != nil {
		return err
	}

	cid := cfg.Identity.PeerID
511
	if cid == "" {
512
		return errors.New("identity was not set in config (was 'ipfs init' run?)")
513 514
	}
	if len(cid) == 0 {
515
		return errors.New("no peer ID in config! (was 'ipfs init' run?)")
516 517
	}

518 519 520
	n.Identity = peer.ID(b58.Decode(cid))
	return nil
}
521

522 523 524 525 526 527 528 529
func (n *IpfsNode) GetKey(name string) (ic.PrivKey, error) {
	if name == "self" {
		return n.PrivateKey, nil
	} else {
		return n.Repo.Keystore().Get(name)
	}
}

Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
530
func (n *IpfsNode) LoadPrivateKey() error {
531
	if n.Identity == "" || n.Peerstore == nil {
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
532
		return errors.New("loaded private key out of order.")
533 534
	}

535
	if n.PrivateKey != nil {
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
536
		return errors.New("private key already loaded")
537 538
	}

539 540 541 542 543 544
	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
545
	if err != nil {
546
		return err
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
547
	}
548

549 550
	n.PrivateKey = sk
	n.Peerstore.AddPrivKey(n.Identity, n.PrivateKey)
Jeromy's avatar
Jeromy committed
551 552 553 554
	n.Peerstore.AddPubKey(n.Identity, sk.GetPublic())
	return nil
}

Jeromy's avatar
Jeromy committed
555
func (n *IpfsNode) loadBootstrapPeers() ([]pstore.PeerInfo, error) {
556 557 558 559 560 561
	cfg, err := n.Repo.Config()
	if err != nil {
		return nil, err
	}

	parsed, err := cfg.BootstrapPeers()
562 563 564 565 566 567
	if err != nil {
		return nil, err
	}
	return toPeerInfos(parsed), nil
}

Jeromy's avatar
Jeromy committed
568
func (n *IpfsNode) loadFilesRoot() error {
Jeromy's avatar
Jeromy committed
569
	dsk := ds.NewKey("/local/filesroot")
Jeromy's avatar
Jeromy committed
570 571
	pf := func(ctx context.Context, c *cid.Cid) error {
		return n.Repo.Datastore().Put(dsk, c.Bytes())
Jeromy's avatar
Jeromy committed
572 573
	}

574
	var nd *merkledag.ProtoNode
Jeromy's avatar
Jeromy committed
575 576 577 578
	val, err := n.Repo.Datastore().Get(dsk)

	switch {
	case err == ds.ErrNotFound || val == nil:
579
		nd = ft.EmptyDirNode()
Jeromy's avatar
Jeromy committed
580 581 582 583 584
		_, 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
585 586 587 588 589
		c, err := cid.Cast(val.([]byte))
		if err != nil {
			return err
		}

590
		rnd, err := n.DAG.Get(n.Context(), c)
Jeromy's avatar
Jeromy committed
591 592 593
		if err != nil {
			return fmt.Errorf("error loading filesroot from DAG: %s", err)
		}
594 595 596 597 598 599 600

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

		nd = pbnd
Jeromy's avatar
Jeromy committed
601 602 603 604 605 606 607 608 609 610 611 612 613
	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
614 615 616
// 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
617
func (n *IpfsNode) SetupOfflineRouting() error {
618 619 620 621
	if n.Routing != nil {
		// Routing was already set up
		return nil
	}
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
622
	err := n.LoadPrivateKey()
Jeromy's avatar
Jeromy committed
623 624 625 626 627
	if err != nil {
		return err
	}

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

629 630 631 632 633 634
	size, err := n.getCacheSize()
	if err != nil {
		return err
	}

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

636
	return nil
637 638 639 640
}

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

645 646 647 648
	id2, err := peer.IDFromPrivateKey(sk)
	if err != nil {
		return nil, err
	}
649

650 651
	if id2 != id {
		return nil, fmt.Errorf("private key in config does not match id: %s != %s", id, id2)
652 653
	}

654
	return sk, nil
655
}
656

657
func listenAddresses(cfg *config.Config) ([]ma.Multiaddr, error) {
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
658 659 660
	var listen []ma.Multiaddr
	for _, addr := range cfg.Addresses.Swarm {
		maddr, err := ma.NewMultiaddr(addr)
661
		if err != nil {
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
662
			return nil, fmt.Errorf("Failure to parse config.Addresses.Swarm: %s", cfg.Addresses.Swarm)
663
		}
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
664
		listen = append(listen, maddr)
665 666 667 668
	}

	return listen, nil
}
669

670
type HostOption func(ctx context.Context, id peer.ID, ps pstore.Peerstore, bwr metrics.Reporter, fs []*net.IPNet, tpt smux.Transport) (p2phost.Host, error)
Jeromy's avatar
Jeromy committed
671 672 673

var DefaultHostOption HostOption = constructPeerHost

674
// isolates the complex initialization steps
675
func constructPeerHost(ctx context.Context, id peer.ID, ps pstore.Peerstore, bwr metrics.Reporter, fs []*net.IPNet, tpt smux.Transport) (p2phost.Host, error) {
676 677

	// no addresses to begin with. we'll start later.
678
	swrm, err := swarm.NewSwarmWithProtector(ctx, nil, id, ps, nil, tpt, bwr)
679
	if err != nil {
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
680
		return nil, err
681
	}
682

683 684
	network := (*swarm.Network)(swrm)

685 686 687 688
	for _, f := range fs {
		network.Swarm().Filters.AddDialFilter(f)
	}

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

691 692 693 694 695 696 697
	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
698
		return err
699 700
	}

701
	// make sure we error out if our config does not have addresses we can use
702
	log.Debugf("Config.Addresses.Swarm:%s", listenAddrs)
703
	filteredAddrs := addrutil.FilterUsableAddrs(listenAddrs)
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
704
	log.Debugf("Config.Addresses.Swarm:%s (filtered)", filteredAddrs)
705
	if len(filteredAddrs) < 1 {
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
706
		return fmt.Errorf("addresses in config not usable: %s", listenAddrs)
707 708
	}

709 710 711
	// Actually start listening:
	if err := host.Network().Listen(filteredAddrs...); err != nil {
		return err
712 713
	}

714
	// list out our addresses
715
	addrs, err := host.Network().InterfaceListenAddresses()
716
	if err != nil {
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
717
		return err
718
	}
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
719
	log.Infof("Swarm listening at: %s", addrs)
720
	return nil
721
}
722

Jeromy's avatar
Jeromy committed
723
func constructDHTRouting(ctx context.Context, host p2phost.Host, dstore repo.Datastore) (routing.IpfsRouting, error) {
724
	dhtRouting := dht.NewDHT(ctx, host, dstore)
725
	dhtRouting.Validator[IpnsValidatorTag] = namesys.IpnsRecordValidator
726
	dhtRouting.Selector[IpnsValidatorTag] = namesys.IpnsSelectorFunc
727 728
	return dhtRouting, nil
}
Jeromy's avatar
Jeromy committed
729

Jeromy's avatar
Jeromy committed
730 731 732 733 734 735 736
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
737
type RoutingOption func(context.Context, p2phost.Host, repo.Datastore) (routing.IpfsRouting, error)
Jeromy's avatar
Jeromy committed
738

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

741
var DHTOption RoutingOption = constructDHTRouting
Jeromy's avatar
Jeromy committed
742
var DHTClientOption RoutingOption = constructClientDHTRouting
743
var NilRouterOption RoutingOption = nilrouting.ConstructNilRouting