core.go 18 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 23 24 25
	discovery "gx/ipfs/QmQ7iWUfqrLEoJwsoNdrZu4625bKyhZCi4Sh6MfjywEfbG/go-libp2p/p2p/discovery"
	p2pbhost "gx/ipfs/QmQ7iWUfqrLEoJwsoNdrZu4625bKyhZCi4Sh6MfjywEfbG/go-libp2p/p2p/host/basic"
	rhost "gx/ipfs/QmQ7iWUfqrLEoJwsoNdrZu4625bKyhZCi4Sh6MfjywEfbG/go-libp2p/p2p/host/routed"
	ping "gx/ipfs/QmQ7iWUfqrLEoJwsoNdrZu4625bKyhZCi4Sh6MfjywEfbG/go-libp2p/p2p/protocol/ping"
26
	goprocess "gx/ipfs/QmSF8fPo3jgVBAy8fpdjjYqgG87dkJgUprRBHRd2tmfgpP/goprocess"
Jeromy's avatar
Jeromy committed
27
	mamask "gx/ipfs/QmSMZwvs3n4GBikZ7hKzT17c3bk65FmyZo2JqtJ16swqCv/multiaddr-filter"
Jeromy's avatar
Jeromy committed
28
	logging "gx/ipfs/QmSpJByNKFX1sCsHBEp3R73FL4NF6FnQTEGyNAXHm2GS52/go-log"
Jeromy's avatar
Jeromy committed
29
	b58 "gx/ipfs/QmT8rehPR3F6bmwL6zjUN8XpiDBFFpMP2myPdC6ApsWfJf/go-base58"
30
	swarm "gx/ipfs/QmTsTADtVpWhp2CYpAqDJWYSK94gMzhE77mQKbp18oC4PK/go-libp2p-swarm"
31 32
	ma "gx/ipfs/QmUAQaWbKxGCUTuoQVvvicbQNZ9APF5pDGWyAZSe93AtKH/go-multiaddr"
	addrutil "gx/ipfs/QmVDnc2zvyQm8LhT72n22THcshvH7j3qPMnhvjerQER62T/go-addr-util"
33
	floodsub "gx/ipfs/QmVxGRDh6aKFYKwiW8WMMD2dsQnAuaZfBjF9Q7HQNuEk5G/floodsub"
34 35
	metrics "gx/ipfs/QmWpTXhTkpoCDEm9twJd5Rc9jFwy61emzxneeJzrVMfjGF/go-libp2p-metrics"
	pstore "gx/ipfs/QmXXCcQ7CLg5a81Ui9TTR35QcR4y7ZyihxwfjqaHfUVcVo/go-libp2p-peerstore"
36
	cid "gx/ipfs/QmXfiyr2RWEXpVDdaYnD2HNiBk6UBddsvEP4RPfXb6nGqY/go-cid"
George Antoniadis's avatar
George Antoniadis committed
37
	ds "gx/ipfs/QmbzuUusHqaLLoNTDEVLcSF6vZDHZDLPC7p4bztRvvkXxU/go-datastore"
38 39 40
	p2phost "gx/ipfs/QmdML3R42PRSwnt46jSuEts9bHSqLctVYEjJqMR3UYV8ki/go-libp2p-host"
	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
	routing "gx/ipfs/QmQKEgGgYCDyk8VNY6A65FpuE4YwbspvjXHco1rdb75PVc/go-libp2p-routing"
45
	dht "gx/ipfs/QmaWzyiqs7sUayh2G1EaovupWrA1qdKXqRMYA97ruU1xS5/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 79 80 81 82
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
83
// IpfsNode is IPFS Core module. It represents an IPFS instance.
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
84 85
type IpfsNode struct {

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

89
	Repo repo.Repo
90 91

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

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

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

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

119 120
	proc goprocess.Process
	ctx  context.Context
121

122
	mode mode
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
123 124
}

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

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

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

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

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

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

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

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

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

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

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

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

206
	return n.Bootstrap(DefaultBootstrapConfig)
207 208
}

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

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

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

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

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

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

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

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

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

266 267 268
	return nil
}

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

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

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

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

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

		n.IpnsRepub.Interval = d
	}

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

319 320 321
	return nil
}

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

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

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

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

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

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

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

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

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

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

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

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

407
func (n *IpfsNode) Bootstrap(cfg BootstrapConfig) error {
408 409

	// TODO what should return value be when in offlineMode?
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
410 411 412 413
	if n.Routing == nil {
		return nil
	}

414 415 416 417 418 419 420
	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
421
		cfg.BootstrapPeers = func() []pstore.PeerInfo {
422
			ps, err := n.loadBootstrapPeers()
423
			if err != nil {
424
				log.Warning("failed to parse bootstrap peers from config")
425 426 427 428 429 430 431 432 433
				return nil
			}
			return ps
		}
	}

	var err error
	n.Bootstrapper, err = Bootstrap(n, cfg)
	return err
434 435
}

436 437
func (n *IpfsNode) loadID() error {
	if n.Identity != "" {
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
438
		return errors.New("identity already loaded")
439 440
	}

441 442 443 444 445 446
	cfg, err := n.Repo.Config()
	if err != nil {
		return err
	}

	cid := cfg.Identity.PeerID
447
	if cid == "" {
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
448
		return errors.New("Identity was not set in config (was ipfs init run?)")
449 450
	}
	if len(cid) == 0 {
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
451
		return errors.New("No peer ID in config! (was ipfs init run?)")
452 453
	}

454 455 456
	n.Identity = peer.ID(b58.Decode(cid))
	return nil
}
457

Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
458
func (n *IpfsNode) LoadPrivateKey() error {
459
	if n.Identity == "" || n.Peerstore == nil {
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
460
		return errors.New("loaded private key out of order.")
461 462
	}

463
	if n.PrivateKey != nil {
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
464
		return errors.New("private key already loaded")
465 466
	}

467 468 469 470 471 472
	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
473
	if err != nil {
474
		return err
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
475
	}
476

477 478
	n.PrivateKey = sk
	n.Peerstore.AddPrivKey(n.Identity, n.PrivateKey)
Jeromy's avatar
Jeromy committed
479 480 481 482
	n.Peerstore.AddPubKey(n.Identity, sk.GetPublic())
	return nil
}

Jeromy's avatar
Jeromy committed
483
func (n *IpfsNode) loadBootstrapPeers() ([]pstore.PeerInfo, error) {
484 485 486 487 488 489
	cfg, err := n.Repo.Config()
	if err != nil {
		return nil, err
	}

	parsed, err := cfg.BootstrapPeers()
490 491 492 493 494 495
	if err != nil {
		return nil, err
	}
	return toPeerInfos(parsed), nil
}

Jeromy's avatar
Jeromy committed
496
func (n *IpfsNode) loadFilesRoot() error {
Jeromy's avatar
Jeromy committed
497
	dsk := ds.NewKey("/local/filesroot")
Jeromy's avatar
Jeromy committed
498 499
	pf := func(ctx context.Context, c *cid.Cid) error {
		return n.Repo.Datastore().Put(dsk, c.Bytes())
Jeromy's avatar
Jeromy committed
500 501
	}

502
	var nd *merkledag.ProtoNode
Jeromy's avatar
Jeromy committed
503 504 505 506
	val, err := n.Repo.Datastore().Get(dsk)

	switch {
	case err == ds.ErrNotFound || val == nil:
507
		nd = ft.EmptyDirNode()
Jeromy's avatar
Jeromy committed
508 509 510 511 512
		_, 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
513 514 515 516 517
		c, err := cid.Cast(val.([]byte))
		if err != nil {
			return err
		}

518
		rnd, err := n.DAG.Get(n.Context(), c)
Jeromy's avatar
Jeromy committed
519 520 521
		if err != nil {
			return fmt.Errorf("error loading filesroot from DAG: %s", err)
		}
522 523 524 525 526 527 528

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

		nd = pbnd
Jeromy's avatar
Jeromy committed
529 530 531 532 533 534 535 536 537 538 539 540 541
	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
542 543 544
// 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
545
func (n *IpfsNode) SetupOfflineRouting() error {
546 547 548 549
	if n.Routing != nil {
		// Routing was already set up
		return nil
	}
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
550
	err := n.LoadPrivateKey()
Jeromy's avatar
Jeromy committed
551 552 553 554 555
	if err != nil {
		return err
	}

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

557 558 559 560 561 562
	size, err := n.getCacheSize()
	if err != nil {
		return err
	}

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

564
	return nil
565 566 567 568
}

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

573 574 575 576
	id2, err := peer.IDFromPrivateKey(sk)
	if err != nil {
		return nil, err
	}
577

578 579
	if id2 != id {
		return nil, fmt.Errorf("private key in config does not match id: %s != %s", id, id2)
580 581
	}

582
	return sk, nil
583
}
584

585
func listenAddresses(cfg *config.Config) ([]ma.Multiaddr, error) {
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
586 587 588
	var listen []ma.Multiaddr
	for _, addr := range cfg.Addresses.Swarm {
		maddr, err := ma.NewMultiaddr(addr)
589
		if err != nil {
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
590
			return nil, fmt.Errorf("Failure to parse config.Addresses.Swarm: %s", cfg.Addresses.Swarm)
591
		}
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
592
		listen = append(listen, maddr)
593 594 595 596
	}

	return listen, nil
}
597

Jeromy's avatar
Jeromy committed
598
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
599 600 601

var DefaultHostOption HostOption = constructPeerHost

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

	// no addresses to begin with. we'll start later.
Jeromy's avatar
Jeromy committed
606
	network, err := swarm.NewNetwork(ctx, nil, id, ps, bwr)
607
	if err != nil {
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
608
		return nil, err
609
	}
610

611 612 613 614
	for _, f := range fs {
		network.Swarm().Filters.AddDialFilter(f)
	}

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

617 618 619 620 621 622 623
	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
624
		return err
625 626
	}

627
	// make sure we error out if our config does not have addresses we can use
628
	log.Debugf("Config.Addresses.Swarm:%s", listenAddrs)
629
	filteredAddrs := addrutil.FilterUsableAddrs(listenAddrs)
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
630
	log.Debugf("Config.Addresses.Swarm:%s (filtered)", filteredAddrs)
631
	if len(filteredAddrs) < 1 {
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
632
		return fmt.Errorf("addresses in config not usable: %s", listenAddrs)
633 634
	}

635 636 637
	// Actually start listening:
	if err := host.Network().Listen(filteredAddrs...); err != nil {
		return err
638 639
	}

640
	// list out our addresses
641
	addrs, err := host.Network().InterfaceListenAddresses()
642
	if err != nil {
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
643
		return err
644
	}
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
645
	log.Infof("Swarm listening at: %s", addrs)
646
	return nil
647
}
648

Jeromy's avatar
Jeromy committed
649
func constructDHTRouting(ctx context.Context, host p2phost.Host, dstore repo.Datastore) (routing.IpfsRouting, error) {
650
	dhtRouting := dht.NewDHT(ctx, host, dstore)
651
	dhtRouting.Validator[IpnsValidatorTag] = namesys.IpnsRecordValidator
652
	dhtRouting.Selector[IpnsValidatorTag] = namesys.IpnsSelectorFunc
653 654
	return dhtRouting, nil
}
Jeromy's avatar
Jeromy committed
655

Jeromy's avatar
Jeromy committed
656 657 658 659 660 661 662
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
663
type RoutingOption func(context.Context, p2phost.Host, repo.Datastore) (routing.IpfsRouting, error)
Jeromy's avatar
Jeromy committed
664

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

667
var DHTOption RoutingOption = constructDHTRouting
Jeromy's avatar
Jeromy committed
668
var DHTClientOption RoutingOption = constructClientDHTRouting
669
var NilRouterOption RoutingOption = nilrouting.ConstructNilRouting