core.go 21.4 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 (
Jakub Sztandera's avatar
Jakub Sztandera committed
13
	"bytes"
14
	"context"
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
15
	"errors"
Juan Batiz-Benet's avatar
go fmt  
Juan Batiz-Benet committed
16
	"fmt"
17
	"io"
18
	"io/ioutil"
Jeromy's avatar
Jeromy committed
19
	"net"
20 21
	"os"
	"strings"
22
	"time"
23

24 25
	bstore "github.com/ipfs/go-ipfs/blocks/blockstore"
	bserv "github.com/ipfs/go-ipfs/blockservice"
26
	ptp "github.com/ipfs/go-ipfs/ptp"
27 28 29 30
	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"
31
	filestore "github.com/ipfs/go-ipfs/filestore"
32 33 34 35 36 37 38 39 40 41 42 43
	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
44

45
	routing "gx/ipfs/QmNdaQ8itUU9jEZUwTsG4gHMaPmRfi6FEe89QjQAFbep3M/go-libp2p-routing"
46 47
	ic "gx/ipfs/QmP1DfoUjiWH2ZBo1PBH6FupdBucbDepx3HpWmEY6JMUpY/go-libp2p-crypto"
	ipnet "gx/ipfs/QmPsBptED6X43GYg3347TAUruN3UfsAhaGTP9xbinYX7uf/go-libp2p-interface-pnet"
48
	mplex "gx/ipfs/QmQ3UABWTgK78utKeiVXaH9BrjC7Ydn1pRuwqnWHT3p4zh/go-smux-multiplex"
49 50 51 52 53
	discovery "gx/ipfs/QmQA5mdxru8Bh6dpC9PJfSkumqnmHgJX7knxSgBo5Lpime/go-libp2p/p2p/discovery"
	p2pbhost "gx/ipfs/QmQA5mdxru8Bh6dpC9PJfSkumqnmHgJX7knxSgBo5Lpime/go-libp2p/p2p/host/basic"
	rhost "gx/ipfs/QmQA5mdxru8Bh6dpC9PJfSkumqnmHgJX7knxSgBo5Lpime/go-libp2p/p2p/host/routed"
	identify "gx/ipfs/QmQA5mdxru8Bh6dpC9PJfSkumqnmHgJX7knxSgBo5Lpime/go-libp2p/p2p/protocol/identify"
	ping "gx/ipfs/QmQA5mdxru8Bh6dpC9PJfSkumqnmHgJX7knxSgBo5Lpime/go-libp2p/p2p/protocol/ping"
54
	mssmux "gx/ipfs/QmRVYfZ7tWNHPBzWiG6KWGzvT2hcGems8srihsQE29x1U5/go-smux-multistream"
Jeromy's avatar
Jeromy committed
55
	ds "gx/ipfs/QmRWDav6mzWseLWeYfVd5fvUKiVe9xNH29YfMF438fG364/go-datastore"
56
	dht "gx/ipfs/QmRmroYSdievxnjiuy99C8BzShNstdEWcEF3LQHF7fUbez/go-libp2p-kad-dht"
57
	goprocess "gx/ipfs/QmSF8fPo3jgVBAy8fpdjjYqgG87dkJgUprRBHRd2tmfgpP/goprocess"
Jeromy's avatar
Jeromy committed
58
	mamask "gx/ipfs/QmSMZwvs3n4GBikZ7hKzT17c3bk65FmyZo2JqtJ16swqCv/multiaddr-filter"
Jeromy's avatar
Jeromy committed
59
	logging "gx/ipfs/QmSpJByNKFX1sCsHBEp3R73FL4NF6FnQTEGyNAXHm2GS52/go-log"
Jeromy's avatar
Jeromy committed
60
	b58 "gx/ipfs/QmT8rehPR3F6bmwL6zjUN8XpiDBFFpMP2myPdC6ApsWfJf/go-base58"
Lars Gierth's avatar
Lars Gierth committed
61
	pnet "gx/ipfs/QmTJoXQ24GqDf9MqAUwf3vW38HG6ahE9S7GzZoRMEeE8Kc/go-libp2p-pnet"
62 63 64
	floodsub "gx/ipfs/QmUpeULWfmtsgCnfuRN3BHsfhHvBxNphoYh4La4CMxGt2Z/floodsub"
	p2phost "gx/ipfs/QmUywuGNZoUKV8B9iyvup9bPkLiMrhTsyVMkeSXW5VxAfC/go-libp2p-host"
	swarm "gx/ipfs/QmVkDnNm71vYyY6s6rXwtmyDYis3WkKyrEhMECwT6R12uJ/go-libp2p-swarm"
65
	u "gx/ipfs/QmWbjfz3u6HkAdPh34dgPchGbQjob6LXLhAeCGii2TX69n/go-ipfs-util"
66
	pstore "gx/ipfs/QmXZSd1qR5BxZkPyuwfT5jpqQFScZccoZvDneXsKzCNHWX/go-libp2p-peerstore"
67 68
	cid "gx/ipfs/QmYhQaCYEcaPPjxJX7YcPcVKkQfRy6sJ7B3XmGFk82XYdQ/go-cid"
	addrutil "gx/ipfs/QmbH3urJHTrZSUETgvQRriWM6mMFqyNSwCqnhknxfSGVWv/go-addr-util"
69
	yamux "gx/ipfs/Qmbn7RYyWzBVXiUp9jZ1dA4VADHy9DtS7iZLwfhEUQvm3U/go-smux-yamux"
70 71
	ma "gx/ipfs/QmcyqRMCAXVtYPS4DiBrA7sezL9rRGfW8Ctx7cywL4TXJj/go-multiaddr"
	peer "gx/ipfs/QmdS9KpbDyPrieswibZhkod1oXqRwZJrUPzxCofAMWpFGq/go-libp2p-peer"
72
	metrics "gx/ipfs/QmdibiN2wzuuXXz4JvqQ1ZGW3eUkoAy1AWznHFau6iePCc/go-libp2p-metrics"
73
	smux "gx/ipfs/QmeZBgYBHvxMukGK5ojg28BCNLB9SeXqT7XXg6o7r2GbJy/go-stream-muxer"
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
74 75
)

Jeromy's avatar
Jeromy committed
76
const IpnsValidatorTag = "ipns"
77
const kSizeBlockstoreWriteCache = 100
78
const kReprovideFrequency = time.Hour * 12
79
const discoveryConnTimeout = time.Second * 30
Jeromy's avatar
Jeromy committed
80

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

83 84 85 86 87
type mode int

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

93 94 95 96
func init() {
	identify.ClientVersion = "go-ipfs/" + config.CurrentVersionNumber + "/" + config.CurrentCommit
}

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

100
	// Self
101
	Identity peer.ID // the local node's identity
102

103
	Repo repo.Repo
104 105

	// Local node
Jakub Sztandera's avatar
Jakub Sztandera committed
106 107 108 109
	Pinning        pin.Pinner // the pinning manager
	Mounts         Mounts     // current mount state, if any.
	PrivateKey     ic.PrivKey // the local node's private Key
	PNetFingerpint []byte     // fingerprint of private network
110 111

	// Services
Jeromy's avatar
Jeromy committed
112
	Peerstore  pstore.Peerstore     // storage for other Peer instances
113
	Blockstore bstore.GCBlockstore  // the block store (lower level)
114 115 116
	Filestore  *filestore.Filestore // the filestore blockstore
	BaseBlocks bstore.Blockstore    // the raw blockstore, no filestore wrapping
	GCLocker   bstore.GCLocker      // the locker used to protect the blockstore during gc
117
	Blocks     bserv.BlockService   // the block service, get/add blocks.
118 119
	DAG        merkledag.DAGService // the merkle dag service, get/add objects.
	Resolver   *path.Resolver       // the path resolution system
Jeromy's avatar
Jeromy committed
120
	Reporter   metrics.Reporter
121
	Discovery  discovery.Service
Jeromy's avatar
Jeromy committed
122
	FilesRoot  *mfs.Root
123 124

	// Online
125 126 127 128 129
	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
Jeromy's avatar
Jeromy committed
130 131
	Ping         *ping.PingService
	Reprovider   *rp.Reprovider // the value reprovider system
Jeromy's avatar
Jeromy committed
132
	IpnsRepub    *ipnsrp.Republisher
133

Jeromy's avatar
Jeromy committed
134
	Floodsub *floodsub.PubSub
135
	PTP      *ptp.PTP
Jeromy's avatar
Jeromy committed
136

137 138
	proc goprocess.Process
	ctx  context.Context
139

Jeromy's avatar
Jeromy committed
140
	mode         mode
141
	localModeSet bool
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
142 143
}

144 145 146 147 148 149 150 151
// 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
}

152
func (n *IpfsNode) startOnlineServices(ctx context.Context, routingOption RoutingOption, hostOption HostOption, do DiscoveryOption, pubsub, mplex bool) error {
153 154

	if n.PeerHost != nil { // already online.
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
155
		return errors.New("node already online")
156 157 158
	}

	// load private key
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
159
	if err := n.LoadPrivateKey(); err != nil {
160 161 162
		return err
	}

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

177 178 179 180 181
	if !cfg.Swarm.DisableBandwidthMetrics {
		// Set reporter
		n.Reporter = metrics.NewBandwidthCounter()
	}

182 183
	tpt := makeSmuxTransport(mplex)

Jakub Sztandera's avatar
Jakub Sztandera committed
184 185 186 187 188 189 190 191 192 193 194 195
	swarmkey, err := n.Repo.SwarmKey()
	if err != nil {
		return err
	}

	var protec ipnet.Protector
	if swarmkey != nil {
		protec, err = pnet.NewProtector(bytes.NewReader(swarmkey))
		if err != nil {
			return err
		}
		n.PNetFingerpint = protec.Fingerprint()
196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213
		go func() {
			t := time.NewTicker(30 * time.Second)
			<-t.C // swallow one tick
			for {
				select {
				case <-t.C:
					if ph := n.PeerHost; ph != nil {
						if len(ph.Network().Peers()) == 0 {
							log.Warning("We are in private network and have no peers.")
							log.Warning("This might be configuration mistake.")
						}
					}
				case <-n.Process().Closing():
					t.Stop()
					return
				}
			}
		}()
Jakub Sztandera's avatar
Jakub Sztandera committed
214 215 216
	}

	peerhost, err := hostOption(ctx, n.Identity, n.Peerstore, n.Reporter,
Kevin Atkinson's avatar
Kevin Atkinson committed
217
		addrfilter, tpt, protec, &ConstructPeerHostOpts{DisableNatPortMap: cfg.Swarm.DisableNatPortMap})
218
	if err != nil {
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
219
		return err
220 221
	}

222
	if err := n.startOnlineServicesWithHost(ctx, peerhost, routingOption); err != nil {
223
		return err
224 225 226
	}

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

231
	n.Reprovider = rp.NewReprovider(n.Routing, n.Blockstore)
232 233 234 235 236 237 238 239 240 241 242 243 244 245

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

Jeromy's avatar
Jeromy committed
247 248 249
	if pubsub {
		n.Floodsub = floodsub.NewFloodSub(ctx, peerhost)
	}
Jeromy's avatar
Jeromy committed
250

251
	n.PTP = ptp.NewPTP()
252

253
	// setup local discovery
Jeromy's avatar
Jeromy committed
254
	if do != nil {
Jeromy's avatar
Jeromy committed
255
		service, err := do(ctx, n.PeerHost)
Jeromy's avatar
Jeromy committed
256
		if err != nil {
Jeromy's avatar
Jeromy committed
257 258 259 260
			log.Error("mdns error: ", err)
		} else {
			service.RegisterNotifee(n)
			n.Discovery = service
Jeromy's avatar
Jeromy committed
261
		}
262 263
	}

264
	return n.Bootstrap(DefaultBootstrapConfig)
265 266
}

267 268 269 270 271 272 273 274 275 276 277 278
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,
	}

279 280 281 282
	if os.Getenv("YAMUX_DEBUG") != "" {
		ymxtpt.LogOutput = os.Stderr
	}

283 284 285 286 287 288 289 290 291 292 293 294 295 296
	mstpt.AddTransport("/yamux/1.0.0", ymxtpt)

	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
297 298
func setupDiscoveryOption(d config.Discovery) DiscoveryOption {
	if d.MDNS.Enabled {
Jeromy's avatar
Jeromy committed
299
		return func(ctx context.Context, h p2phost.Host) (discovery.Service, error) {
Jeromy's avatar
Jeromy committed
300 301 302
			if d.MDNS.Interval == 0 {
				d.MDNS.Interval = 5
			}
Jeromy's avatar
Jeromy committed
303
			return discovery.NewMdnsService(ctx, h, time.Duration(d.MDNS.Interval)*time.Second)
Jeromy's avatar
Jeromy committed
304 305 306 307 308
		}
	}
	return nil
}

Jeromy's avatar
Jeromy committed
309
func (n *IpfsNode) HandlePeerFound(p pstore.PeerInfo) {
310
	log.Warning("trying peer info: ", p)
311
	ctx, cancel := context.WithTimeout(n.Context(), discoveryConnTimeout)
rht's avatar
rht committed
312
	defer cancel()
313
	if err := n.PeerHost.Connect(ctx, p); err != nil {
314 315 316 317
		log.Warning("Failed to connect to peer found by discovery: ", err)
	}
}

318 319
// startOnlineServicesWithHost  is the set of services which need to be
// initialized with the host and _before_ we start listening.
320
func (n *IpfsNode) startOnlineServicesWithHost(ctx context.Context, host p2phost.Host, routingOption RoutingOption) error {
321
	// setup diagnostics service
Jeromy's avatar
Jeromy committed
322
	n.Ping = ping.NewPingService(host)
323 324

	// setup routing service
325
	r, err := routingOption(ctx, host, n.Repo.Datastore())
Jeromy's avatar
Jeromy committed
326
	if err != nil {
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
327
		return err
328
	}
Jeromy's avatar
Jeromy committed
329
	n.Routing = r
330

331 332 333
	// Wrap standard peer host with routing system to allow unknown peer lookups
	n.PeerHost = rhost.Wrap(host, n.Routing)

334 335
	// setup exchange service
	const alwaysSendToPeer = true // use YesManStrategy
336
	bitswapNetwork := bsnet.NewFromIpfsHost(n.PeerHost, n.Routing)
337 338
	n.Exchange = bitswap.New(ctx, n.Identity, bitswapNetwork, n.Blockstore, alwaysSendToPeer)

339 340 341 342 343
	size, err := n.getCacheSize()
	if err != nil {
		return err
	}

344
	// setup name system
345
	n.Namesys = namesys.NewNameSystem(n.Routing, n.Repo.Datastore(), size)
346

Jeromy's avatar
Jeromy committed
347
	// setup ipns republishing
348 349 350 351
	err = n.setupIpnsRepublisher()
	if err != nil {
		return err
	}
Jeromy's avatar
Jeromy committed
352

353 354 355
	return nil
}

356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372
// 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
}

373
func (n *IpfsNode) setupIpnsRepublisher() error {
Jeromy's avatar
Jeromy committed
374 375 376 377
	cfg, err := n.Repo.Config()
	if err != nil {
		return err
	}
378 379 380 381

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

Jeromy's avatar
Jeromy committed
382 383 384 385 386 387
	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)
		}

388
		if !u.Debug && (d < time.Minute || d > (time.Hour*24)) {
Jeromy's avatar
Jeromy committed
389 390 391 392 393 394
			return fmt.Errorf("config setting IPNS.RepublishPeriod is not between 1min and 1day: %s", d)
		}

		n.IpnsRepub.Interval = d
	}

395 396 397 398 399 400 401 402 403
	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
404 405
	n.Process().Go(n.IpnsRepub.Run)

406 407 408
	return nil
}

409 410 411 412 413 414 415 416 417 418 419 420
// 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 {
421 422 423
	if n.ctx == nil {
		n.ctx = context.TODO()
	}
424 425 426
	return n.ctx
}

427 428
// teardown closes owned children. If any errors occur, this function returns
// the first error.
Brian Tiger Chow's avatar
Brian Tiger Chow committed
429
func (n *IpfsNode) teardown() error {
430
	log.Debug("core is shutting down...")
431 432
	// 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
433 434
	var closers []io.Closer

435
	// NOTE: The order that objects are added(closed) matters, if an object
Jeromy's avatar
Jeromy committed
436 437 438 439 440
	// 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
441
	}
442

443 444 445 446
	if n.Exchange != nil {
		closers = append(closers, n.Exchange)
	}

447
	if n.Mounts.Ipfs != nil && !n.Mounts.Ipfs.IsActive() {
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
448 449
		closers = append(closers, mount.Closer(n.Mounts.Ipfs))
	}
450
	if n.Mounts.Ipns != nil && !n.Mounts.Ipns.IsActive() {
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
451 452 453
		closers = append(closers, mount.Closer(n.Mounts.Ipns))
	}

Jeromy's avatar
Jeromy committed
454 455 456 457
	if dht, ok := n.Routing.(*dht.IpfsDHT); ok {
		closers = append(closers, dht.Process())
	}

Jeromy's avatar
Jeromy committed
458 459 460 461
	if n.Blocks != nil {
		closers = append(closers, n.Blocks)
	}

Jeromy's avatar
Jeromy committed
462 463
	if n.Bootstrapper != nil {
		closers = append(closers, n.Bootstrapper)
464 465
	}

Jeromy's avatar
Jeromy committed
466 467
	if n.PeerHost != nil {
		closers = append(closers, n.PeerHost)
468
	}
469

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

473
	var errs []error
474
	for _, closer := range closers {
475 476
		if err := closer.Close(); err != nil {
			errs = append(errs, err)
477 478 479 480
		}
	}
	if len(errs) > 0 {
		return errs[0]
Brian Tiger Chow's avatar
Brian Tiger Chow committed
481 482
	}
	return nil
Brian Tiger Chow's avatar
Brian Tiger Chow committed
483 484
}

Brian Tiger Chow's avatar
Brian Tiger Chow committed
485
func (n *IpfsNode) OnlineMode() bool {
486 487 488 489 490 491
	switch n.mode {
	case onlineMode:
		return true
	default:
		return false
	}
Brian Tiger Chow's avatar
Brian Tiger Chow committed
492 493
}

494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513
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
	}
}

514
func (n *IpfsNode) Bootstrap(cfg BootstrapConfig) error {
515 516

	// TODO what should return value be when in offlineMode?
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
517 518 519 520
	if n.Routing == nil {
		return nil
	}

521 522 523 524 525 526 527
	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
528
		cfg.BootstrapPeers = func() []pstore.PeerInfo {
529
			ps, err := n.loadBootstrapPeers()
530
			if err != nil {
531
				log.Warning("failed to parse bootstrap peers from config")
532 533 534 535 536 537 538 539 540
				return nil
			}
			return ps
		}
	}

	var err error
	n.Bootstrapper, err = Bootstrap(n, cfg)
	return err
541 542
}

543 544
func (n *IpfsNode) loadID() error {
	if n.Identity != "" {
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
545
		return errors.New("identity already loaded")
546 547
	}

548 549 550 551 552 553
	cfg, err := n.Repo.Config()
	if err != nil {
		return err
	}

	cid := cfg.Identity.PeerID
554
	if cid == "" {
555
		return errors.New("identity was not set in config (was 'ipfs init' run?)")
556 557
	}
	if len(cid) == 0 {
558
		return errors.New("no peer ID in config! (was 'ipfs init' run?)")
559 560
	}

561 562 563
	n.Identity = peer.ID(b58.Decode(cid))
	return nil
}
564

565 566 567 568 569 570 571 572
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
573
func (n *IpfsNode) LoadPrivateKey() error {
574
	if n.Identity == "" || n.Peerstore == nil {
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
575
		return errors.New("loaded private key out of order.")
576 577
	}

578
	if n.PrivateKey != nil {
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
579
		return errors.New("private key already loaded")
580 581
	}

582 583 584 585 586 587
	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
588
	if err != nil {
589
		return err
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
590
	}
591

592 593
	n.PrivateKey = sk
	n.Peerstore.AddPrivKey(n.Identity, n.PrivateKey)
Jeromy's avatar
Jeromy committed
594 595 596 597
	n.Peerstore.AddPubKey(n.Identity, sk.GetPublic())
	return nil
}

Jeromy's avatar
Jeromy committed
598
func (n *IpfsNode) loadBootstrapPeers() ([]pstore.PeerInfo, error) {
599 600 601 602 603 604
	cfg, err := n.Repo.Config()
	if err != nil {
		return nil, err
	}

	parsed, err := cfg.BootstrapPeers()
605 606 607 608 609 610
	if err != nil {
		return nil, err
	}
	return toPeerInfos(parsed), nil
}

Jeromy's avatar
Jeromy committed
611
func (n *IpfsNode) loadFilesRoot() error {
Jeromy's avatar
Jeromy committed
612
	dsk := ds.NewKey("/local/filesroot")
Jeromy's avatar
Jeromy committed
613 614
	pf := func(ctx context.Context, c *cid.Cid) error {
		return n.Repo.Datastore().Put(dsk, c.Bytes())
Jeromy's avatar
Jeromy committed
615 616
	}

617
	var nd *merkledag.ProtoNode
Jeromy's avatar
Jeromy committed
618 619 620 621
	val, err := n.Repo.Datastore().Get(dsk)

	switch {
	case err == ds.ErrNotFound || val == nil:
622
		nd = ft.EmptyDirNode()
Jeromy's avatar
Jeromy committed
623 624 625 626 627
		_, 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
628 629 630 631 632
		c, err := cid.Cast(val.([]byte))
		if err != nil {
			return err
		}

633
		rnd, err := n.DAG.Get(n.Context(), c)
Jeromy's avatar
Jeromy committed
634 635 636
		if err != nil {
			return fmt.Errorf("error loading filesroot from DAG: %s", err)
		}
637 638 639 640 641 642 643

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

		nd = pbnd
Jeromy's avatar
Jeromy committed
644 645 646 647 648 649 650 651 652 653 654 655 656
	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
657 658 659
// 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
660
func (n *IpfsNode) SetupOfflineRouting() error {
661 662 663 664
	if n.Routing != nil {
		// Routing was already set up
		return nil
	}
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
665
	err := n.LoadPrivateKey()
Jeromy's avatar
Jeromy committed
666 667 668 669 670
	if err != nil {
		return err
	}

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

672 673 674 675 676 677
	size, err := n.getCacheSize()
	if err != nil {
		return err
	}

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

679
	return nil
680 681 682 683
}

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

688 689 690 691
	id2, err := peer.IDFromPrivateKey(sk)
	if err != nil {
		return nil, err
	}
692

693 694
	if id2 != id {
		return nil, fmt.Errorf("private key in config does not match id: %s != %s", id, id2)
695 696
	}

697
	return sk, nil
698
}
699

700
func listenAddresses(cfg *config.Config) ([]ma.Multiaddr, error) {
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
701 702 703
	var listen []ma.Multiaddr
	for _, addr := range cfg.Addresses.Swarm {
		maddr, err := ma.NewMultiaddr(addr)
704
		if err != nil {
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
705
			return nil, fmt.Errorf("Failure to parse config.Addresses.Swarm: %s", cfg.Addresses.Swarm)
706
		}
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
707
		listen = append(listen, maddr)
708 709 710 711
	}

	return listen, nil
}
712

Kevin Atkinson's avatar
Kevin Atkinson committed
713 714 715 716 717
type ConstructPeerHostOpts struct {
	DisableNatPortMap bool
}

type HostOption func(ctx context.Context, id peer.ID, ps pstore.Peerstore, bwr metrics.Reporter, fs []*net.IPNet, tpt smux.Transport, protc ipnet.Protector, opts *ConstructPeerHostOpts) (p2phost.Host, error)
Jeromy's avatar
Jeromy committed
718 719 720

var DefaultHostOption HostOption = constructPeerHost

721
// isolates the complex initialization steps
Kevin Atkinson's avatar
Kevin Atkinson committed
722
func constructPeerHost(ctx context.Context, id peer.ID, ps pstore.Peerstore, bwr metrics.Reporter, fs []*net.IPNet, tpt smux.Transport, protec ipnet.Protector, opts *ConstructPeerHostOpts) (p2phost.Host, error) {
723 724

	// no addresses to begin with. we'll start later.
Jakub Sztandera's avatar
Jakub Sztandera committed
725
	swrm, err := swarm.NewSwarmWithProtector(ctx, nil, id, ps, protec, tpt, bwr)
726
	if err != nil {
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
727
		return nil, err
728
	}
729

730 731
	network := (*swarm.Network)(swrm)

732 733 734 735
	for _, f := range fs {
		network.Swarm().Filters.AddDialFilter(f)
	}

Kevin Atkinson's avatar
Kevin Atkinson committed
736 737 738 739 740 741
	hostOpts := []interface{}{bwr}
	if !opts.DisableNatPortMap {
		hostOpts = append(hostOpts, p2pbhost.NATPortMap)
	}

	host := p2pbhost.New(network, hostOpts...)
Jeromy's avatar
Jeromy committed
742

743 744 745 746 747 748 749
	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
750
		return err
751 752
	}

753
	// make sure we error out if our config does not have addresses we can use
754
	log.Debugf("Config.Addresses.Swarm:%s", listenAddrs)
755
	filteredAddrs := addrutil.FilterUsableAddrs(listenAddrs)
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
756
	log.Debugf("Config.Addresses.Swarm:%s (filtered)", filteredAddrs)
757
	if len(filteredAddrs) < 1 {
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
758
		return fmt.Errorf("addresses in config not usable: %s", listenAddrs)
759 760
	}

761 762 763
	// Actually start listening:
	if err := host.Network().Listen(filteredAddrs...); err != nil {
		return err
764 765
	}

766
	// list out our addresses
767
	addrs, err := host.Network().InterfaceListenAddresses()
768
	if err != nil {
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
769
		return err
770
	}
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
771
	log.Infof("Swarm listening at: %s", addrs)
772
	return nil
773
}
774

Jeromy's avatar
Jeromy committed
775
func constructDHTRouting(ctx context.Context, host p2phost.Host, dstore repo.Datastore) (routing.IpfsRouting, error) {
776
	dhtRouting := dht.NewDHT(ctx, host, dstore)
777
	dhtRouting.Validator[IpnsValidatorTag] = namesys.IpnsRecordValidator
778
	dhtRouting.Selector[IpnsValidatorTag] = namesys.IpnsSelectorFunc
779 780
	return dhtRouting, nil
}
Jeromy's avatar
Jeromy committed
781

Jeromy's avatar
Jeromy committed
782 783 784 785 786 787 788
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
789
type RoutingOption func(context.Context, p2phost.Host, repo.Datastore) (routing.IpfsRouting, error)
Jeromy's avatar
Jeromy committed
790

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

793
var DHTOption RoutingOption = constructDHTRouting
Jeromy's avatar
Jeromy committed
794
var DHTClientOption RoutingOption = constructClientDHTRouting
795
var NilRouterOption RoutingOption = nilrouting.ConstructNilRouting