core.go 21.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 (
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 26 27 28 29
	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"
30
	filestore "github.com/ipfs/go-ipfs/filestore"
31 32 33 34 35 36 37 38 39 40 41 42
	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
43

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

Jeromy's avatar
Jeromy committed
75
const IpnsValidatorTag = "ipns"
76 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 135
	Floodsub *floodsub.PubSub

136 137
	proc goprocess.Process
	ctx  context.Context
138

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

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

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

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

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

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

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

181 182
	tpt := makeSmuxTransport(mplex)

Jakub Sztandera's avatar
Jakub Sztandera committed
183 184 185 186 187 188 189 190 191 192 193 194
	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()
195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212
		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
213 214 215
	}

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

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

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

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

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

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

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

261
	return n.Bootstrap(DefaultBootstrapConfig)
262 263
}

264 265 266 267 268 269 270 271 272 273 274 275
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,
	}

276 277 278 279
	if os.Getenv("YAMUX_DEBUG") != "" {
		ymxtpt.LogOutput = os.Stderr
	}

280 281 282 283 284 285 286 287 288 289 290 291 292 293
	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
294 295
func setupDiscoveryOption(d config.Discovery) DiscoveryOption {
	if d.MDNS.Enabled {
Jeromy's avatar
Jeromy committed
296
		return func(ctx context.Context, h p2phost.Host) (discovery.Service, error) {
Jeromy's avatar
Jeromy committed
297 298 299
			if d.MDNS.Interval == 0 {
				d.MDNS.Interval = 5
			}
Jeromy's avatar
Jeromy committed
300
			return discovery.NewMdnsService(ctx, h, time.Duration(d.MDNS.Interval)*time.Second)
Jeromy's avatar
Jeromy committed
301 302 303 304 305
		}
	}
	return nil
}

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

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

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

328 329 330
	// Wrap standard peer host with routing system to allow unknown peer lookups
	n.PeerHost = rhost.Wrap(host, n.Routing)

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

336 337 338 339 340
	size, err := n.getCacheSize()
	if err != nil {
		return err
	}

341
	// setup name system
342
	n.Namesys = namesys.NewNameSystem(n.Routing, n.Repo.Datastore(), size)
343

Jeromy's avatar
Jeromy committed
344
	// setup ipns republishing
345
	return n.setupIpnsRepublisher()
346 347
}

348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364
// 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
}

365
func (n *IpfsNode) setupIpnsRepublisher() error {
Jeromy's avatar
Jeromy committed
366 367 368 369
	cfg, err := n.Repo.Config()
	if err != nil {
		return err
	}
370 371 372 373

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

Jeromy's avatar
Jeromy committed
374 375 376 377 378 379
	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)
		}

380
		if !u.Debug && (d < time.Minute || d > (time.Hour*24)) {
Jeromy's avatar
Jeromy committed
381 382 383 384 385 386
			return fmt.Errorf("config setting IPNS.RepublishPeriod is not between 1min and 1day: %s", d)
		}

		n.IpnsRepub.Interval = d
	}

387 388 389 390 391 392 393 394 395
	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
396 397
	n.Process().Go(n.IpnsRepub.Run)

398 399 400
	return nil
}

401 402 403 404 405 406 407 408 409 410 411 412
// 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 {
413 414 415
	if n.ctx == nil {
		n.ctx = context.TODO()
	}
416 417 418
	return n.ctx
}

419 420
// teardown closes owned children. If any errors occur, this function returns
// the first error.
Brian Tiger Chow's avatar
Brian Tiger Chow committed
421
func (n *IpfsNode) teardown() error {
422
	log.Debug("core is shutting down...")
423 424
	// 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
425 426
	var closers []io.Closer

427
	// NOTE: The order that objects are added(closed) matters, if an object
Jeromy's avatar
Jeromy committed
428 429 430 431 432
	// 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
433
	}
434

435 436 437 438
	if n.Exchange != nil {
		closers = append(closers, n.Exchange)
	}

439
	if n.Mounts.Ipfs != nil && !n.Mounts.Ipfs.IsActive() {
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
440 441
		closers = append(closers, mount.Closer(n.Mounts.Ipfs))
	}
442
	if n.Mounts.Ipns != nil && !n.Mounts.Ipns.IsActive() {
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
443 444 445
		closers = append(closers, mount.Closer(n.Mounts.Ipns))
	}

Jeromy's avatar
Jeromy committed
446 447 448 449
	if dht, ok := n.Routing.(*dht.IpfsDHT); ok {
		closers = append(closers, dht.Process())
	}

Jeromy's avatar
Jeromy committed
450 451 452 453
	if n.Blocks != nil {
		closers = append(closers, n.Blocks)
	}

Jeromy's avatar
Jeromy committed
454 455
	if n.Bootstrapper != nil {
		closers = append(closers, n.Bootstrapper)
456 457
	}

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

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

465
	var errs []error
466
	for _, closer := range closers {
467 468
		if err := closer.Close(); err != nil {
			errs = append(errs, err)
469 470 471 472
		}
	}
	if len(errs) > 0 {
		return errs[0]
Brian Tiger Chow's avatar
Brian Tiger Chow committed
473 474
	}
	return nil
Brian Tiger Chow's avatar
Brian Tiger Chow committed
475 476
}

Brian Tiger Chow's avatar
Brian Tiger Chow committed
477
func (n *IpfsNode) OnlineMode() bool {
478 479 480 481 482 483
	switch n.mode {
	case onlineMode:
		return true
	default:
		return false
	}
Brian Tiger Chow's avatar
Brian Tiger Chow committed
484 485
}

486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505
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
	}
}

506
func (n *IpfsNode) Bootstrap(cfg BootstrapConfig) error {
507 508

	// TODO what should return value be when in offlineMode?
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
509 510 511 512
	if n.Routing == nil {
		return nil
	}

513 514 515 516 517 518 519
	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
520
		cfg.BootstrapPeers = func() []pstore.PeerInfo {
521
			ps, err := n.loadBootstrapPeers()
522
			if err != nil {
523
				log.Warning("failed to parse bootstrap peers from config")
524 525 526 527 528 529 530 531 532
				return nil
			}
			return ps
		}
	}

	var err error
	n.Bootstrapper, err = Bootstrap(n, cfg)
	return err
533 534
}

535 536
func (n *IpfsNode) loadID() error {
	if n.Identity != "" {
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
537
		return errors.New("identity already loaded")
538 539
	}

540 541 542 543 544 545
	cfg, err := n.Repo.Config()
	if err != nil {
		return err
	}

	cid := cfg.Identity.PeerID
546
	if cid == "" {
547
		return errors.New("identity was not set in config (was 'ipfs init' run?)")
548 549
	}
	if len(cid) == 0 {
550
		return errors.New("no peer ID in config! (was 'ipfs init' run?)")
551 552
	}

553 554 555
	n.Identity = peer.ID(b58.Decode(cid))
	return nil
}
556

557 558 559 560 561 562 563 564
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
565
func (n *IpfsNode) LoadPrivateKey() error {
566
	if n.Identity == "" || n.Peerstore == nil {
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
567
		return errors.New("loaded private key out of order.")
568 569
	}

570
	if n.PrivateKey != nil {
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
571
		return errors.New("private key already loaded")
572 573
	}

574 575 576 577 578 579
	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
580
	if err != nil {
581
		return err
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
582
	}
583

584 585
	n.PrivateKey = sk
	n.Peerstore.AddPrivKey(n.Identity, n.PrivateKey)
Jeromy's avatar
Jeromy committed
586 587 588 589
	n.Peerstore.AddPubKey(n.Identity, sk.GetPublic())
	return nil
}

Jeromy's avatar
Jeromy committed
590
func (n *IpfsNode) loadBootstrapPeers() ([]pstore.PeerInfo, error) {
591 592 593 594 595 596
	cfg, err := n.Repo.Config()
	if err != nil {
		return nil, err
	}

	parsed, err := cfg.BootstrapPeers()
597 598 599 600 601 602
	if err != nil {
		return nil, err
	}
	return toPeerInfos(parsed), nil
}

Jeromy's avatar
Jeromy committed
603
func (n *IpfsNode) loadFilesRoot() error {
Jeromy's avatar
Jeromy committed
604
	dsk := ds.NewKey("/local/filesroot")
Jeromy's avatar
Jeromy committed
605 606
	pf := func(ctx context.Context, c *cid.Cid) error {
		return n.Repo.Datastore().Put(dsk, c.Bytes())
Jeromy's avatar
Jeromy committed
607 608
	}

609
	var nd *merkledag.ProtoNode
Jeromy's avatar
Jeromy committed
610 611 612 613
	val, err := n.Repo.Datastore().Get(dsk)

	switch {
	case err == ds.ErrNotFound || val == nil:
614
		nd = ft.EmptyDirNode()
Jeromy's avatar
Jeromy committed
615 616 617 618 619
		_, 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
620 621 622 623 624
		c, err := cid.Cast(val.([]byte))
		if err != nil {
			return err
		}

625
		rnd, err := n.DAG.Get(n.Context(), c)
Jeromy's avatar
Jeromy committed
626 627 628
		if err != nil {
			return fmt.Errorf("error loading filesroot from DAG: %s", err)
		}
629 630 631 632 633 634 635

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

		nd = pbnd
Jeromy's avatar
Jeromy committed
636 637 638 639 640 641 642 643 644 645 646 647 648
	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
649 650 651
// 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
652
func (n *IpfsNode) SetupOfflineRouting() error {
653 654 655 656
	if n.Routing != nil {
		// Routing was already set up
		return nil
	}
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
657
	err := n.LoadPrivateKey()
Jeromy's avatar
Jeromy committed
658 659 660 661 662
	if err != nil {
		return err
	}

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

664 665 666 667 668 669
	size, err := n.getCacheSize()
	if err != nil {
		return err
	}

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

671
	return nil
672 673 674 675
}

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

680 681 682 683
	id2, err := peer.IDFromPrivateKey(sk)
	if err != nil {
		return nil, err
	}
684

685 686
	if id2 != id {
		return nil, fmt.Errorf("private key in config does not match id: %s != %s", id, id2)
687 688
	}

689
	return sk, nil
690
}
691

692
func listenAddresses(cfg *config.Config) ([]ma.Multiaddr, error) {
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
693 694 695
	var listen []ma.Multiaddr
	for _, addr := range cfg.Addresses.Swarm {
		maddr, err := ma.NewMultiaddr(addr)
696
		if err != nil {
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
697
			return nil, fmt.Errorf("Failure to parse config.Addresses.Swarm: %s", cfg.Addresses.Swarm)
698
		}
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
699
		listen = append(listen, maddr)
700 701 702 703
	}

	return listen, nil
}
704

Kevin Atkinson's avatar
Kevin Atkinson committed
705 706 707 708 709
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
710 711 712

var DefaultHostOption HostOption = constructPeerHost

713
// isolates the complex initialization steps
Kevin Atkinson's avatar
Kevin Atkinson committed
714
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) {
715 716

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

722 723
	network := (*swarm.Network)(swrm)

724 725 726 727
	for _, f := range fs {
		network.Swarm().Filters.AddDialFilter(f)
	}

Kevin Atkinson's avatar
Kevin Atkinson committed
728 729 730 731 732 733
	hostOpts := []interface{}{bwr}
	if !opts.DisableNatPortMap {
		hostOpts = append(hostOpts, p2pbhost.NATPortMap)
	}

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

735 736 737 738 739 740 741
	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
742
		return err
743 744
	}

745
	// make sure we error out if our config does not have addresses we can use
746
	log.Debugf("Config.Addresses.Swarm:%s", listenAddrs)
747
	filteredAddrs := addrutil.FilterUsableAddrs(listenAddrs)
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
748
	log.Debugf("Config.Addresses.Swarm:%s (filtered)", filteredAddrs)
749
	if len(filteredAddrs) < 1 {
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
750
		return fmt.Errorf("addresses in config not usable: %s", listenAddrs)
751 752
	}

753 754 755
	// Actually start listening:
	if err := host.Network().Listen(filteredAddrs...); err != nil {
		return err
756 757
	}

758
	// list out our addresses
759
	addrs, err := host.Network().InterfaceListenAddresses()
760
	if err != nil {
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
761
		return err
762
	}
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
763
	log.Infof("Swarm listening at: %s", addrs)
764
	return nil
765
}
766

Jeromy's avatar
Jeromy committed
767
func constructDHTRouting(ctx context.Context, host p2phost.Host, dstore repo.Datastore) (routing.IpfsRouting, error) {
768
	dhtRouting := dht.NewDHT(ctx, host, dstore)
769
	dhtRouting.Validator[IpnsValidatorTag] = namesys.IpnsRecordValidator
770
	dhtRouting.Selector[IpnsValidatorTag] = namesys.IpnsSelectorFunc
771 772
	return dhtRouting, nil
}
Jeromy's avatar
Jeromy committed
773

Jeromy's avatar
Jeromy committed
774 775 776 777 778 779 780
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
781
type RoutingOption func(context.Context, p2phost.Host, repo.Datastore) (routing.IpfsRouting, error)
Jeromy's avatar
Jeromy committed
782

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

785
var DHTOption RoutingOption = constructDHTRouting
Jeromy's avatar
Jeromy committed
786
var DHTClientOption RoutingOption = constructClientDHTRouting
787
var NilRouterOption RoutingOption = nilrouting.ConstructNilRouting