core.go 22.7 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
	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"
Łukasz Magiera's avatar
Łukasz Magiera committed
36
	p2p "github.com/ipfs/go-ipfs/p2p"
37 38 39 40 41 42 43
	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

Steven Allen's avatar
Steven Allen committed
45
	pnet "gx/ipfs/QmNMCAuxnQFHLGWcvay3DmVFrKuY6Y2nsc9vzsf4gVouJV/go-libp2p-pnet"
46 47
	pstore "gx/ipfs/QmPgDWmTmuzvP7QE5zwo1TmjbJme9pmZHNujB2453jkCTr/go-libp2p-peerstore"
	routing "gx/ipfs/QmPjTrrSfE6TzLv6ya6VWhGcCgPrUAdcgrDcQyRDX2VyW1/go-libp2p-routing"
48
	mafilter "gx/ipfs/QmQBB2dQLmQHJgs2gqZ3iqL2XiuCtUCvXzWt5kMXDf5Zcr/go-maddr-filter"
Steven Allen's avatar
Steven Allen committed
49
	floodsub "gx/ipfs/QmQCm4CY8hXwYnaEZ5E2iRWFmWWX9xGCTABySyLbfyqXNh/floodsub"
50
	ipnet "gx/ipfs/QmQq9YzmdFdWNTDdArueGyD7L5yyiRQigrRHJnTGkxcEjT/go-libp2p-interface-pnet"
Steven Allen's avatar
Steven Allen committed
51
	p2phost "gx/ipfs/QmRNyPNJGNCaZyYonJj7owciWTsMd9gRfEKmZY3o6xwN3h/go-libp2p-host"
52
	mssmux "gx/ipfs/QmRVYfZ7tWNHPBzWiG6KWGzvT2hcGems8srihsQE29x1U5/go-smux-multistream"
53
	goprocess "gx/ipfs/QmSF8fPo3jgVBAy8fpdjjYqgG87dkJgUprRBHRd2tmfgpP/goprocess"
Jeromy's avatar
Jeromy committed
54
	mamask "gx/ipfs/QmSMZwvs3n4GBikZ7hKzT17c3bk65FmyZo2JqtJ16swqCv/multiaddr-filter"
55
	u "gx/ipfs/QmSU6eubNdhXjFBJBSksTp8kv8YRub8mGAPv8tVJHmL2EU/go-ipfs-util"
Jeromy's avatar
Jeromy committed
56
	logging "gx/ipfs/QmSpJByNKFX1sCsHBEp3R73FL4NF6FnQTEGyNAXHm2GS52/go-log"
Jeromy's avatar
Jeromy committed
57
	b58 "gx/ipfs/QmT8rehPR3F6bmwL6zjUN8XpiDBFFpMP2myPdC6ApsWfJf/go-base58"
58
	cid "gx/ipfs/QmTprEaAA2A9bst5XH7exuyi5KzNMK3SEDNN8rBDnKWcUS/go-cid"
59
	ds "gx/ipfs/QmVSase1JP7cq9QkPT46oNwdp9pT6kBkG3oqS14y3QcZjG/go-datastore"
60 61 62
	metrics "gx/ipfs/QmVjRAPfRtResCMCE4eBqr4Beoa6A89P1YweG9wUS6RqUL/go-libp2p-metrics"
	ma "gx/ipfs/QmXY77cVe7rVRQXZZQRioukUM7aRW3BTcAgJe12MCtb3Ji/go-multiaddr"
	peer "gx/ipfs/QmXYjuNuxVzXKJCfWasQk1RqkhVLDM9jtUKhqc2WPQmFSB/go-libp2p-peer"
Steven Allen's avatar
Steven Allen committed
63 64 65 66 67
	discovery "gx/ipfs/QmZPBrKq6S1fdYaRAzYZivJL12QkUqHwnNzF9wC8VXC4bo/go-libp2p/p2p/discovery"
	p2pbhost "gx/ipfs/QmZPBrKq6S1fdYaRAzYZivJL12QkUqHwnNzF9wC8VXC4bo/go-libp2p/p2p/host/basic"
	rhost "gx/ipfs/QmZPBrKq6S1fdYaRAzYZivJL12QkUqHwnNzF9wC8VXC4bo/go-libp2p/p2p/host/routed"
	identify "gx/ipfs/QmZPBrKq6S1fdYaRAzYZivJL12QkUqHwnNzF9wC8VXC4bo/go-libp2p/p2p/protocol/identify"
	ping "gx/ipfs/QmZPBrKq6S1fdYaRAzYZivJL12QkUqHwnNzF9wC8VXC4bo/go-libp2p/p2p/protocol/ping"
68
	ic "gx/ipfs/QmaPbCnUMBohSGo3KnxEa2bHqyJVVeEEcwtqJAYxerieBo/go-libp2p-crypto"
Steven Allen's avatar
Steven Allen committed
69
	swarm "gx/ipfs/Qmb9GA1fMGfXdLBwJA9h3SRpr7C4AXUhpWgZzVmqcDJi1P/go-libp2p-swarm"
70
	yamux "gx/ipfs/Qmbn7RYyWzBVXiUp9jZ1dA4VADHy9DtS7iZLwfhEUQvm3U/go-smux-yamux"
Steven Allen's avatar
Steven Allen committed
71
	dht "gx/ipfs/QmcKxeQomXUjo54VwisTiXeic5FFBknwUPtT7yRWvmPD1D/go-libp2p-kad-dht"
Steven Allen's avatar
Steven Allen committed
72
	addrutil "gx/ipfs/Qmcx4KoZ91XS6izLjdkujAMAunAS1YuTgfgASgYaZF5GkR/go-addr-util"
Steven Allen's avatar
Steven Allen committed
73
	mplex "gx/ipfs/Qme65bXDcjpTRnBc8jhqRGbj9saiwUfxhuPqSns35mrGYZ/go-smux-multiplex"
74
	smux "gx/ipfs/QmeZBgYBHvxMukGK5ojg28BCNLB9SeXqT7XXg6o7r2GbJy/go-stream-muxer"
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
75 76
)

Jeromy's avatar
Jeromy committed
77
const IpnsValidatorTag = "ipns"
78

79
const kReprovideFrequency = time.Hour * 12
80
const discoveryConnTimeout = time.Second * 30
Jeromy's avatar
Jeromy committed
81

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

84 85 86 87
type mode int

const (
	// zero value is not a valid mode, must be explicitly set
88
	localMode mode = iota
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
Łukasz Magiera's avatar
Łukasz Magiera committed
135
	P2P      *p2p.P2P
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 217 218 219 220 221 222 223 224
	addrsFactory, err := makeAddrsFactory(cfg.Addresses)
	if err != nil {
		return err
	}

	hostopts := &ConstructPeerHostOpts{
		AddrsFactory:      addrsFactory,
		DisableNatPortMap: cfg.Swarm.DisableNatPortMap,
	}
Jakub Sztandera's avatar
Jakub Sztandera committed
225
	peerhost, err := hostOption(ctx, n.Identity, n.Peerstore, n.Reporter,
226
		addrfilter, tpt, protec, hostopts)
227
	if err != nil {
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
228
		return err
229 230
	}

231
	if err := n.startOnlineServicesWithHost(ctx, peerhost, routingOption); err != nil {
232
		return err
233 234 235
	}

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

240
	n.Reprovider = rp.NewReprovider(n.Routing, n.Blockstore)
241 242 243 244 245 246 247 248 249 250 251 252 253 254

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

Jeromy's avatar
Jeromy committed
256 257 258
	if pubsub {
		n.Floodsub = floodsub.NewFloodSub(ctx, peerhost)
	}
Jeromy's avatar
Jeromy committed
259

Łukasz Magiera's avatar
Łukasz Magiera committed
260
	n.P2P = p2p.NewP2P(n.Identity, n.PeerHost, n.Peerstore)
261

262
	// setup local discovery
Jeromy's avatar
Jeromy committed
263
	if do != nil {
Jeromy's avatar
Jeromy committed
264
		service, err := do(ctx, n.PeerHost)
Jeromy's avatar
Jeromy committed
265
		if err != nil {
Jeromy's avatar
Jeromy committed
266 267 268 269
			log.Error("mdns error: ", err)
		} else {
			service.RegisterNotifee(n)
			n.Discovery = service
Jeromy's avatar
Jeromy committed
270
		}
271 272
	}

273
	return n.Bootstrap(DefaultBootstrapConfig)
274 275
}

276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321
func makeAddrsFactory(cfg config.Addresses) (p2pbhost.AddrsFactory, error) {
	var annAddrs []ma.Multiaddr
	for _, addr := range cfg.Announce {
		maddr, err := ma.NewMultiaddr(addr)
		if err != nil {
			return nil, err
		}
		annAddrs = append(annAddrs, maddr)
	}

	filters := mafilter.NewFilters()
	noAnnAddrs := map[string]bool{}
	for _, addr := range cfg.NoAnnounce {
		f, err := mamask.NewMask(addr)
		if err == nil {
			filters.AddDialFilter(f)
			continue
		}
		maddr, err := ma.NewMultiaddr(addr)
		if err != nil {
			return nil, err
		}
		noAnnAddrs[maddr.String()] = true
	}

	return func(allAddrs []ma.Multiaddr) []ma.Multiaddr {
		var addrs []ma.Multiaddr
		if len(annAddrs) > 0 {
			addrs = annAddrs
		} else {
			addrs = allAddrs
		}

		var out []ma.Multiaddr
		for _, maddr := range addrs {
			// check for exact matches
			ok, _ := noAnnAddrs[maddr.String()]
			// check for /ipcidr matches
			if !ok && !filters.AddrBlocked(maddr) {
				out = append(out, maddr)
			}
		}
		return out
	}, nil
}

322 323 324 325 326 327 328 329 330 331 332 333
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,
	}

334 335 336 337
	if os.Getenv("YAMUX_DEBUG") != "" {
		ymxtpt.LogOutput = os.Stderr
	}

338 339 340 341 342 343 344 345 346 347 348 349 350 351
	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
352 353
func setupDiscoveryOption(d config.Discovery) DiscoveryOption {
	if d.MDNS.Enabled {
Jeromy's avatar
Jeromy committed
354
		return func(ctx context.Context, h p2phost.Host) (discovery.Service, error) {
Jeromy's avatar
Jeromy committed
355 356 357
			if d.MDNS.Interval == 0 {
				d.MDNS.Interval = 5
			}
Jeromy's avatar
Jeromy committed
358
			return discovery.NewMdnsService(ctx, h, time.Duration(d.MDNS.Interval)*time.Second)
Jeromy's avatar
Jeromy committed
359 360 361 362 363
		}
	}
	return nil
}

Jeromy's avatar
Jeromy committed
364
func (n *IpfsNode) HandlePeerFound(p pstore.PeerInfo) {
365
	log.Warning("trying peer info: ", p)
366
	ctx, cancel := context.WithTimeout(n.Context(), discoveryConnTimeout)
rht's avatar
rht committed
367
	defer cancel()
368
	if err := n.PeerHost.Connect(ctx, p); err != nil {
369 370 371 372
		log.Warning("Failed to connect to peer found by discovery: ", err)
	}
}

373 374
// startOnlineServicesWithHost  is the set of services which need to be
// initialized with the host and _before_ we start listening.
375
func (n *IpfsNode) startOnlineServicesWithHost(ctx context.Context, host p2phost.Host, routingOption RoutingOption) error {
376
	// setup diagnostics service
Jeromy's avatar
Jeromy committed
377
	n.Ping = ping.NewPingService(host)
378 379

	// setup routing service
380
	r, err := routingOption(ctx, host, n.Repo.Datastore())
Jeromy's avatar
Jeromy committed
381
	if err != nil {
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
382
		return err
383
	}
Jeromy's avatar
Jeromy committed
384
	n.Routing = r
385

386 387 388
	// Wrap standard peer host with routing system to allow unknown peer lookups
	n.PeerHost = rhost.Wrap(host, n.Routing)

389 390
	// setup exchange service
	const alwaysSendToPeer = true // use YesManStrategy
391
	bitswapNetwork := bsnet.NewFromIpfsHost(n.PeerHost, n.Routing)
392 393
	n.Exchange = bitswap.New(ctx, n.Identity, bitswapNetwork, n.Blockstore, alwaysSendToPeer)

394 395 396 397 398
	size, err := n.getCacheSize()
	if err != nil {
		return err
	}

399
	// setup name system
400
	n.Namesys = namesys.NewNameSystem(n.Routing, n.Repo.Datastore(), size)
401

Jeromy's avatar
Jeromy committed
402
	// setup ipns republishing
403
	return n.setupIpnsRepublisher()
404 405
}

406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422
// 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
}

423
func (n *IpfsNode) setupIpnsRepublisher() error {
Jeromy's avatar
Jeromy committed
424 425 426 427
	cfg, err := n.Repo.Config()
	if err != nil {
		return err
	}
428

429
	n.IpnsRepub = ipnsrp.NewRepublisher(n.Routing, n.Repo.Datastore(), n.PrivateKey, n.Repo.Keystore())
430

Jeromy's avatar
Jeromy committed
431 432 433 434 435 436
	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)
		}

437
		if !u.Debug && (d < time.Minute || d > (time.Hour*24)) {
Jeromy's avatar
Jeromy committed
438 439 440 441 442 443
			return fmt.Errorf("config setting IPNS.RepublishPeriod is not between 1min and 1day: %s", d)
		}

		n.IpnsRepub.Interval = d
	}

444 445 446 447 448 449 450 451 452
	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
453 454
	n.Process().Go(n.IpnsRepub.Run)

455 456 457
	return nil
}

458 459 460 461 462 463 464 465 466 467 468 469
// 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 {
470 471 472
	if n.ctx == nil {
		n.ctx = context.TODO()
	}
473 474 475
	return n.ctx
}

476 477
// teardown closes owned children. If any errors occur, this function returns
// the first error.
Brian Tiger Chow's avatar
Brian Tiger Chow committed
478
func (n *IpfsNode) teardown() error {
479
	log.Debug("core is shutting down...")
480 481
	// 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
482 483
	var closers []io.Closer

484
	// NOTE: The order that objects are added(closed) matters, if an object
Jeromy's avatar
Jeromy committed
485 486 487 488 489
	// 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
490
	}
491

492 493 494 495
	if n.Exchange != nil {
		closers = append(closers, n.Exchange)
	}

496
	if n.Mounts.Ipfs != nil && !n.Mounts.Ipfs.IsActive() {
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
497 498
		closers = append(closers, mount.Closer(n.Mounts.Ipfs))
	}
499
	if n.Mounts.Ipns != nil && !n.Mounts.Ipns.IsActive() {
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
500 501 502
		closers = append(closers, mount.Closer(n.Mounts.Ipns))
	}

Jeromy's avatar
Jeromy committed
503 504 505 506
	if dht, ok := n.Routing.(*dht.IpfsDHT); ok {
		closers = append(closers, dht.Process())
	}

Jeromy's avatar
Jeromy committed
507 508 509 510
	if n.Blocks != nil {
		closers = append(closers, n.Blocks)
	}

Jeromy's avatar
Jeromy committed
511 512
	if n.Bootstrapper != nil {
		closers = append(closers, n.Bootstrapper)
513 514
	}

Jeromy's avatar
Jeromy committed
515 516
	if n.PeerHost != nil {
		closers = append(closers, n.PeerHost)
517
	}
518

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

522
	var errs []error
523
	for _, closer := range closers {
524 525
		if err := closer.Close(); err != nil {
			errs = append(errs, err)
526 527 528 529
		}
	}
	if len(errs) > 0 {
		return errs[0]
Brian Tiger Chow's avatar
Brian Tiger Chow committed
530 531
	}
	return nil
Brian Tiger Chow's avatar
Brian Tiger Chow committed
532 533
}

Brian Tiger Chow's avatar
Brian Tiger Chow committed
534
func (n *IpfsNode) OnlineMode() bool {
535 536 537 538 539 540
	switch n.mode {
	case onlineMode:
		return true
	default:
		return false
	}
Brian Tiger Chow's avatar
Brian Tiger Chow committed
541 542
}

543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562
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
	}
}

563
func (n *IpfsNode) Bootstrap(cfg BootstrapConfig) error {
564 565

	// TODO what should return value be when in offlineMode?
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
566 567 568 569
	if n.Routing == nil {
		return nil
	}

570 571 572 573 574 575 576
	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
577
		cfg.BootstrapPeers = func() []pstore.PeerInfo {
578
			ps, err := n.loadBootstrapPeers()
579
			if err != nil {
580
				log.Warning("failed to parse bootstrap peers from config")
581 582 583 584 585 586 587 588 589
				return nil
			}
			return ps
		}
	}

	var err error
	n.Bootstrapper, err = Bootstrap(n, cfg)
	return err
590 591
}

592 593
func (n *IpfsNode) loadID() error {
	if n.Identity != "" {
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
594
		return errors.New("identity already loaded")
595 596
	}

597 598 599 600 601 602
	cfg, err := n.Repo.Config()
	if err != nil {
		return err
	}

	cid := cfg.Identity.PeerID
603
	if cid == "" {
604
		return errors.New("identity was not set in config (was 'ipfs init' run?)")
605 606
	}
	if len(cid) == 0 {
607
		return errors.New("no peer ID in config! (was 'ipfs init' run?)")
608 609
	}

610 611 612
	n.Identity = peer.ID(b58.Decode(cid))
	return nil
}
613

614 615 616 617 618 619 620 621
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
622
func (n *IpfsNode) LoadPrivateKey() error {
623
	if n.Identity == "" || n.Peerstore == nil {
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
624
		return errors.New("loaded private key out of order.")
625 626
	}

627
	if n.PrivateKey != nil {
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
628
		return errors.New("private key already loaded")
629 630
	}

631 632 633 634 635 636
	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
637
	if err != nil {
638
		return err
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
639
	}
640

641 642
	n.PrivateKey = sk
	n.Peerstore.AddPrivKey(n.Identity, n.PrivateKey)
Jeromy's avatar
Jeromy committed
643 644 645 646
	n.Peerstore.AddPubKey(n.Identity, sk.GetPublic())
	return nil
}

Jeromy's avatar
Jeromy committed
647
func (n *IpfsNode) loadBootstrapPeers() ([]pstore.PeerInfo, error) {
648 649 650 651 652 653
	cfg, err := n.Repo.Config()
	if err != nil {
		return nil, err
	}

	parsed, err := cfg.BootstrapPeers()
654 655 656 657 658 659
	if err != nil {
		return nil, err
	}
	return toPeerInfos(parsed), nil
}

Jeromy's avatar
Jeromy committed
660
func (n *IpfsNode) loadFilesRoot() error {
Jeromy's avatar
Jeromy committed
661
	dsk := ds.NewKey("/local/filesroot")
Jeromy's avatar
Jeromy committed
662 663
	pf := func(ctx context.Context, c *cid.Cid) error {
		return n.Repo.Datastore().Put(dsk, c.Bytes())
Jeromy's avatar
Jeromy committed
664 665
	}

666
	var nd *merkledag.ProtoNode
Jeromy's avatar
Jeromy committed
667 668 669 670
	val, err := n.Repo.Datastore().Get(dsk)

	switch {
	case err == ds.ErrNotFound || val == nil:
671
		nd = ft.EmptyDirNode()
Jeromy's avatar
Jeromy committed
672 673 674 675 676
		_, 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
677 678 679 680 681
		c, err := cid.Cast(val.([]byte))
		if err != nil {
			return err
		}

682
		rnd, err := n.DAG.Get(n.Context(), c)
Jeromy's avatar
Jeromy committed
683 684 685
		if err != nil {
			return fmt.Errorf("error loading filesroot from DAG: %s", err)
		}
686 687 688 689 690 691 692

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

		nd = pbnd
Jeromy's avatar
Jeromy committed
693 694 695 696 697 698 699 700 701 702 703 704 705
	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
706 707 708
// 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
709
func (n *IpfsNode) SetupOfflineRouting() error {
710 711 712 713
	if n.Routing != nil {
		// Routing was already set up
		return nil
	}
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
714
	err := n.LoadPrivateKey()
Jeromy's avatar
Jeromy committed
715 716 717 718 719
	if err != nil {
		return err
	}

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

721 722 723 724 725 726
	size, err := n.getCacheSize()
	if err != nil {
		return err
	}

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

728
	return nil
729 730 731 732
}

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

737 738 739 740
	id2, err := peer.IDFromPrivateKey(sk)
	if err != nil {
		return nil, err
	}
741

742 743
	if id2 != id {
		return nil, fmt.Errorf("private key in config does not match id: %s != %s", id, id2)
744 745
	}

746
	return sk, nil
747
}
748

749
func listenAddresses(cfg *config.Config) ([]ma.Multiaddr, error) {
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
750 751 752
	var listen []ma.Multiaddr
	for _, addr := range cfg.Addresses.Swarm {
		maddr, err := ma.NewMultiaddr(addr)
753
		if err != nil {
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
754
			return nil, fmt.Errorf("Failure to parse config.Addresses.Swarm: %s", cfg.Addresses.Swarm)
755
		}
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
756
		listen = append(listen, maddr)
757 758 759 760
	}

	return listen, nil
}
761

Kevin Atkinson's avatar
Kevin Atkinson committed
762 763
type ConstructPeerHostOpts struct {
	DisableNatPortMap bool
764
	AddrsFactory      p2pbhost.AddrsFactory
Kevin Atkinson's avatar
Kevin Atkinson committed
765 766 767
}

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
768 769 770

var DefaultHostOption HostOption = constructPeerHost

771
// isolates the complex initialization steps
Kevin Atkinson's avatar
Kevin Atkinson committed
772
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) {
773 774

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

780 781
	network := (*swarm.Network)(swrm)

782 783 784 785
	for _, f := range fs {
		network.Swarm().Filters.AddDialFilter(f)
	}

Kevin Atkinson's avatar
Kevin Atkinson committed
786 787 788 789
	hostOpts := []interface{}{bwr}
	if !opts.DisableNatPortMap {
		hostOpts = append(hostOpts, p2pbhost.NATPortMap)
	}
790 791 792
	if opts.AddrsFactory != nil {
		hostOpts = append(hostOpts, opts.AddrsFactory)
	}
Kevin Atkinson's avatar
Kevin Atkinson committed
793 794

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

796 797 798 799 800 801 802
	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
803
		return err
804 805
	}

806
	// make sure we error out if our config does not have addresses we can use
807
	log.Debugf("Config.Addresses.Swarm:%s", listenAddrs)
808
	filteredAddrs := addrutil.FilterUsableAddrs(listenAddrs)
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
809
	log.Debugf("Config.Addresses.Swarm:%s (filtered)", filteredAddrs)
810
	if len(filteredAddrs) < 1 {
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
811
		return fmt.Errorf("addresses in config not usable: %s", listenAddrs)
812 813
	}

814 815 816
	// Actually start listening:
	if err := host.Network().Listen(filteredAddrs...); err != nil {
		return err
817 818
	}

819
	// list out our addresses
820
	addrs, err := host.Network().InterfaceListenAddresses()
821
	if err != nil {
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
822
		return err
823
	}
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
824
	log.Infof("Swarm listening at: %s", addrs)
825
	return nil
826
}
827

Jeromy's avatar
Jeromy committed
828
func constructDHTRouting(ctx context.Context, host p2phost.Host, dstore repo.Datastore) (routing.IpfsRouting, error) {
829
	dhtRouting := dht.NewDHT(ctx, host, dstore)
830
	dhtRouting.Validator[IpnsValidatorTag] = namesys.IpnsRecordValidator
831
	dhtRouting.Selector[IpnsValidatorTag] = namesys.IpnsSelectorFunc
832 833
	return dhtRouting, nil
}
Jeromy's avatar
Jeromy committed
834

Jeromy's avatar
Jeromy committed
835 836 837 838 839 840 841
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
842
type RoutingOption func(context.Context, p2phost.Host, repo.Datastore) (routing.IpfsRouting, error)
Jeromy's avatar
Jeromy committed
843

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

846
var DHTOption RoutingOption = constructDHTRouting
Jeromy's avatar
Jeromy committed
847
var DHTClientOption RoutingOption = constructClientDHTRouting
848
var NilRouterOption RoutingOption = nilrouting.ConstructNilRouting