core.go 23.2 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

Jeromy's avatar
Jeromy committed
240 241 242
	if pubsub {
		n.Floodsub = floodsub.NewFloodSub(ctx, peerhost)
	}
Jeromy's avatar
Jeromy committed
243

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

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

257
	return n.Bootstrap(DefaultBootstrapConfig)
258 259
}

Łukasz Magiera's avatar
Łukasz Magiera committed
260 261 262 263 264 265 266 267 268 269 270 271 272 273 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
func (n *IpfsNode) startLateOnlineServices(ctx context.Context) error {
	cfg, err := n.Repo.Config()
	if err != nil {
		return err
	}

	var keyProvider func(context.Context) (<-chan *cid.Cid, error)

	switch cfg.Reprovider.Strategy {
	case "all":
		fallthrough
	case "":
		keyProvider = rp.NewBlockstoreProvider(n.Blockstore)
	case "roots":
		keyProvider = rp.NewPinnedProvider(n.Pinning, n.DAG, true)
	case "pinned":
		keyProvider = rp.NewPinnedProvider(n.Pinning, n.DAG, false)
	default:
		return fmt.Errorf("unknown reprovider strtaegy '%s'", cfg.Reprovider.Strategy)
	}
	n.Reprovider = rp.NewReprovider(n.Routing, keyProvider)

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

	return nil
}

299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344
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
}

345 346 347 348 349 350 351 352 353 354 355 356
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,
	}

357 358 359 360
	if os.Getenv("YAMUX_DEBUG") != "" {
		ymxtpt.LogOutput = os.Stderr
	}

361 362 363 364 365 366 367 368 369 370 371 372 373 374
	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
375 376
func setupDiscoveryOption(d config.Discovery) DiscoveryOption {
	if d.MDNS.Enabled {
Jeromy's avatar
Jeromy committed
377
		return func(ctx context.Context, h p2phost.Host) (discovery.Service, error) {
Jeromy's avatar
Jeromy committed
378 379 380
			if d.MDNS.Interval == 0 {
				d.MDNS.Interval = 5
			}
Jeromy's avatar
Jeromy committed
381
			return discovery.NewMdnsService(ctx, h, time.Duration(d.MDNS.Interval)*time.Second)
Jeromy's avatar
Jeromy committed
382 383 384 385 386
		}
	}
	return nil
}

Jeromy's avatar
Jeromy committed
387
func (n *IpfsNode) HandlePeerFound(p pstore.PeerInfo) {
388
	log.Warning("trying peer info: ", p)
389
	ctx, cancel := context.WithTimeout(n.Context(), discoveryConnTimeout)
rht's avatar
rht committed
390
	defer cancel()
391
	if err := n.PeerHost.Connect(ctx, p); err != nil {
392 393 394 395
		log.Warning("Failed to connect to peer found by discovery: ", err)
	}
}

396 397
// startOnlineServicesWithHost  is the set of services which need to be
// initialized with the host and _before_ we start listening.
398
func (n *IpfsNode) startOnlineServicesWithHost(ctx context.Context, host p2phost.Host, routingOption RoutingOption) error {
399
	// setup diagnostics service
Jeromy's avatar
Jeromy committed
400
	n.Ping = ping.NewPingService(host)
401 402

	// setup routing service
403
	r, err := routingOption(ctx, host, n.Repo.Datastore())
Jeromy's avatar
Jeromy committed
404
	if err != nil {
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
405
		return err
406
	}
Jeromy's avatar
Jeromy committed
407
	n.Routing = r
408

409 410 411
	// Wrap standard peer host with routing system to allow unknown peer lookups
	n.PeerHost = rhost.Wrap(host, n.Routing)

412 413
	// setup exchange service
	const alwaysSendToPeer = true // use YesManStrategy
414
	bitswapNetwork := bsnet.NewFromIpfsHost(n.PeerHost, n.Routing)
415 416
	n.Exchange = bitswap.New(ctx, n.Identity, bitswapNetwork, n.Blockstore, alwaysSendToPeer)

417 418 419 420 421
	size, err := n.getCacheSize()
	if err != nil {
		return err
	}

422
	// setup name system
423
	n.Namesys = namesys.NewNameSystem(n.Routing, n.Repo.Datastore(), size)
424

Jeromy's avatar
Jeromy committed
425
	// setup ipns republishing
426
	return n.setupIpnsRepublisher()
427 428
}

429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445
// 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
}

446
func (n *IpfsNode) setupIpnsRepublisher() error {
Jeromy's avatar
Jeromy committed
447 448 449 450
	cfg, err := n.Repo.Config()
	if err != nil {
		return err
	}
451

452
	n.IpnsRepub = ipnsrp.NewRepublisher(n.Routing, n.Repo.Datastore(), n.PrivateKey, n.Repo.Keystore())
453

Jeromy's avatar
Jeromy committed
454 455 456 457 458 459
	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)
		}

460
		if !u.Debug && (d < time.Minute || d > (time.Hour*24)) {
Jeromy's avatar
Jeromy committed
461 462 463 464 465 466
			return fmt.Errorf("config setting IPNS.RepublishPeriod is not between 1min and 1day: %s", d)
		}

		n.IpnsRepub.Interval = d
	}

467 468 469 470 471 472 473 474 475
	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
476 477
	n.Process().Go(n.IpnsRepub.Run)

478 479 480
	return nil
}

481 482 483 484 485 486 487 488 489 490 491 492
// 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 {
493 494 495
	if n.ctx == nil {
		n.ctx = context.TODO()
	}
496 497 498
	return n.ctx
}

499 500
// teardown closes owned children. If any errors occur, this function returns
// the first error.
Brian Tiger Chow's avatar
Brian Tiger Chow committed
501
func (n *IpfsNode) teardown() error {
502
	log.Debug("core is shutting down...")
503 504
	// 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
505 506
	var closers []io.Closer

507
	// NOTE: The order that objects are added(closed) matters, if an object
Jeromy's avatar
Jeromy committed
508 509 510 511 512
	// 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
513
	}
514

515 516 517 518
	if n.Exchange != nil {
		closers = append(closers, n.Exchange)
	}

519
	if n.Mounts.Ipfs != nil && !n.Mounts.Ipfs.IsActive() {
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
520 521
		closers = append(closers, mount.Closer(n.Mounts.Ipfs))
	}
522
	if n.Mounts.Ipns != nil && !n.Mounts.Ipns.IsActive() {
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
523 524 525
		closers = append(closers, mount.Closer(n.Mounts.Ipns))
	}

Jeromy's avatar
Jeromy committed
526 527 528 529
	if dht, ok := n.Routing.(*dht.IpfsDHT); ok {
		closers = append(closers, dht.Process())
	}

Jeromy's avatar
Jeromy committed
530 531 532 533
	if n.Blocks != nil {
		closers = append(closers, n.Blocks)
	}

Jeromy's avatar
Jeromy committed
534 535
	if n.Bootstrapper != nil {
		closers = append(closers, n.Bootstrapper)
536 537
	}

Jeromy's avatar
Jeromy committed
538 539
	if n.PeerHost != nil {
		closers = append(closers, n.PeerHost)
540
	}
541

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

545
	var errs []error
546
	for _, closer := range closers {
547 548
		if err := closer.Close(); err != nil {
			errs = append(errs, err)
549 550 551 552
		}
	}
	if len(errs) > 0 {
		return errs[0]
Brian Tiger Chow's avatar
Brian Tiger Chow committed
553 554
	}
	return nil
Brian Tiger Chow's avatar
Brian Tiger Chow committed
555 556
}

Brian Tiger Chow's avatar
Brian Tiger Chow committed
557
func (n *IpfsNode) OnlineMode() bool {
558 559 560 561 562 563
	switch n.mode {
	case onlineMode:
		return true
	default:
		return false
	}
Brian Tiger Chow's avatar
Brian Tiger Chow committed
564 565
}

566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585
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
	}
}

586
func (n *IpfsNode) Bootstrap(cfg BootstrapConfig) error {
587 588

	// TODO what should return value be when in offlineMode?
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
589 590 591 592
	if n.Routing == nil {
		return nil
	}

593 594 595 596 597 598 599
	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
600
		cfg.BootstrapPeers = func() []pstore.PeerInfo {
601
			ps, err := n.loadBootstrapPeers()
602
			if err != nil {
603
				log.Warning("failed to parse bootstrap peers from config")
604 605 606 607 608 609 610 611 612
				return nil
			}
			return ps
		}
	}

	var err error
	n.Bootstrapper, err = Bootstrap(n, cfg)
	return err
613 614
}

615 616
func (n *IpfsNode) loadID() error {
	if n.Identity != "" {
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
617
		return errors.New("identity already loaded")
618 619
	}

620 621 622 623 624 625
	cfg, err := n.Repo.Config()
	if err != nil {
		return err
	}

	cid := cfg.Identity.PeerID
626
	if cid == "" {
627
		return errors.New("identity was not set in config (was 'ipfs init' run?)")
628 629
	}
	if len(cid) == 0 {
630
		return errors.New("no peer ID in config! (was 'ipfs init' run?)")
631 632
	}

633 634 635
	n.Identity = peer.ID(b58.Decode(cid))
	return nil
}
636

637 638 639 640 641 642 643 644
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
645
func (n *IpfsNode) LoadPrivateKey() error {
646
	if n.Identity == "" || n.Peerstore == nil {
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
647
		return errors.New("loaded private key out of order.")
648 649
	}

650
	if n.PrivateKey != nil {
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
651
		return errors.New("private key already loaded")
652 653
	}

654 655 656 657 658 659
	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
660
	if err != nil {
661
		return err
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
662
	}
663

664 665
	n.PrivateKey = sk
	n.Peerstore.AddPrivKey(n.Identity, n.PrivateKey)
Jeromy's avatar
Jeromy committed
666 667 668 669
	n.Peerstore.AddPubKey(n.Identity, sk.GetPublic())
	return nil
}

Jeromy's avatar
Jeromy committed
670
func (n *IpfsNode) loadBootstrapPeers() ([]pstore.PeerInfo, error) {
671 672 673 674 675 676
	cfg, err := n.Repo.Config()
	if err != nil {
		return nil, err
	}

	parsed, err := cfg.BootstrapPeers()
677 678 679 680 681 682
	if err != nil {
		return nil, err
	}
	return toPeerInfos(parsed), nil
}

Jeromy's avatar
Jeromy committed
683
func (n *IpfsNode) loadFilesRoot() error {
Jeromy's avatar
Jeromy committed
684
	dsk := ds.NewKey("/local/filesroot")
Jeromy's avatar
Jeromy committed
685 686
	pf := func(ctx context.Context, c *cid.Cid) error {
		return n.Repo.Datastore().Put(dsk, c.Bytes())
Jeromy's avatar
Jeromy committed
687 688
	}

689
	var nd *merkledag.ProtoNode
Jeromy's avatar
Jeromy committed
690 691 692 693
	val, err := n.Repo.Datastore().Get(dsk)

	switch {
	case err == ds.ErrNotFound || val == nil:
694
		nd = ft.EmptyDirNode()
Jeromy's avatar
Jeromy committed
695 696 697 698 699
		_, 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
700 701 702 703 704
		c, err := cid.Cast(val.([]byte))
		if err != nil {
			return err
		}

705
		rnd, err := n.DAG.Get(n.Context(), c)
Jeromy's avatar
Jeromy committed
706 707 708
		if err != nil {
			return fmt.Errorf("error loading filesroot from DAG: %s", err)
		}
709 710 711 712 713 714 715

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

		nd = pbnd
Jeromy's avatar
Jeromy committed
716 717 718 719 720 721 722 723 724 725 726 727 728
	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
729 730 731
// 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
732
func (n *IpfsNode) SetupOfflineRouting() error {
733 734 735 736
	if n.Routing != nil {
		// Routing was already set up
		return nil
	}
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
737
	err := n.LoadPrivateKey()
Jeromy's avatar
Jeromy committed
738 739 740 741 742
	if err != nil {
		return err
	}

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

744 745 746 747 748 749
	size, err := n.getCacheSize()
	if err != nil {
		return err
	}

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

751
	return nil
752 753 754 755
}

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

760 761 762 763
	id2, err := peer.IDFromPrivateKey(sk)
	if err != nil {
		return nil, err
	}
764

765 766
	if id2 != id {
		return nil, fmt.Errorf("private key in config does not match id: %s != %s", id, id2)
767 768
	}

769
	return sk, nil
770
}
771

772
func listenAddresses(cfg *config.Config) ([]ma.Multiaddr, error) {
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
773 774 775
	var listen []ma.Multiaddr
	for _, addr := range cfg.Addresses.Swarm {
		maddr, err := ma.NewMultiaddr(addr)
776
		if err != nil {
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
777
			return nil, fmt.Errorf("Failure to parse config.Addresses.Swarm: %s", cfg.Addresses.Swarm)
778
		}
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
779
		listen = append(listen, maddr)
780 781 782 783
	}

	return listen, nil
}
784

Kevin Atkinson's avatar
Kevin Atkinson committed
785 786
type ConstructPeerHostOpts struct {
	DisableNatPortMap bool
787
	AddrsFactory      p2pbhost.AddrsFactory
Kevin Atkinson's avatar
Kevin Atkinson committed
788 789 790
}

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
791 792 793

var DefaultHostOption HostOption = constructPeerHost

794
// isolates the complex initialization steps
Kevin Atkinson's avatar
Kevin Atkinson committed
795
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) {
796 797

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

803 804
	network := (*swarm.Network)(swrm)

805 806 807 808
	for _, f := range fs {
		network.Swarm().Filters.AddDialFilter(f)
	}

Kevin Atkinson's avatar
Kevin Atkinson committed
809 810 811 812
	hostOpts := []interface{}{bwr}
	if !opts.DisableNatPortMap {
		hostOpts = append(hostOpts, p2pbhost.NATPortMap)
	}
813 814 815
	if opts.AddrsFactory != nil {
		hostOpts = append(hostOpts, opts.AddrsFactory)
	}
Kevin Atkinson's avatar
Kevin Atkinson committed
816 817

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

819 820 821 822 823 824 825
	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
826
		return err
827 828
	}

829
	// make sure we error out if our config does not have addresses we can use
830
	log.Debugf("Config.Addresses.Swarm:%s", listenAddrs)
831
	filteredAddrs := addrutil.FilterUsableAddrs(listenAddrs)
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
832
	log.Debugf("Config.Addresses.Swarm:%s (filtered)", filteredAddrs)
833
	if len(filteredAddrs) < 1 {
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
834
		return fmt.Errorf("addresses in config not usable: %s", listenAddrs)
835 836
	}

837 838 839
	// Actually start listening:
	if err := host.Network().Listen(filteredAddrs...); err != nil {
		return err
840 841
	}

842
	// list out our addresses
843
	addrs, err := host.Network().InterfaceListenAddresses()
844
	if err != nil {
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
845
		return err
846
	}
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
847
	log.Infof("Swarm listening at: %s", addrs)
848
	return nil
849
}
850

Jeromy's avatar
Jeromy committed
851
func constructDHTRouting(ctx context.Context, host p2phost.Host, dstore repo.Datastore) (routing.IpfsRouting, error) {
852
	dhtRouting := dht.NewDHT(ctx, host, dstore)
853
	dhtRouting.Validator[IpnsValidatorTag] = namesys.IpnsRecordValidator
854
	dhtRouting.Selector[IpnsValidatorTag] = namesys.IpnsSelectorFunc
855 856
	return dhtRouting, nil
}
Jeromy's avatar
Jeromy committed
857

Jeromy's avatar
Jeromy committed
858 859 860 861 862 863 864
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
865
type RoutingOption func(context.Context, p2phost.Host, repo.Datastore) (routing.IpfsRouting, error)
Jeromy's avatar
Jeromy committed
866

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

869
var DHTOption RoutingOption = constructDHTRouting
Jeromy's avatar
Jeromy committed
870
var DHTClientOption RoutingOption = constructClientDHTRouting
871
var NilRouterOption RoutingOption = nilrouting.ConstructNilRouting