core.go 27.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 19 20
	"io/ioutil"
	"os"
	"strings"
21
	"time"
22

23
	version "github.com/ipfs/go-ipfs"
24
	rp "github.com/ipfs/go-ipfs/exchange/reprovide"
25
	filestore "github.com/ipfs/go-ipfs/filestore"
26 27 28
	mount "github.com/ipfs/go-ipfs/fuse/mount"
	namesys "github.com/ipfs/go-ipfs/namesys"
	ipnsrp "github.com/ipfs/go-ipfs/namesys/republisher"
Łukasz Magiera's avatar
Łukasz Magiera committed
29
	p2p "github.com/ipfs/go-ipfs/p2p"
30 31
	pin "github.com/ipfs/go-ipfs/pin"
	repo "github.com/ipfs/go-ipfs/repo"
32

Steven Allen's avatar
Steven Allen committed
33 34
	bitswap "gx/ipfs/QmNkxFCmPtr2RQxjZNRCNryLud4L9wMEiBJsLgF14MqTHj/go-bitswap"
	bsnet "gx/ipfs/QmNkxFCmPtr2RQxjZNRCNryLud4L9wMEiBJsLgF14MqTHj/go-bitswap/network"
Steven Allen's avatar
Steven Allen committed
35
	config "gx/ipfs/QmPEpj17FDRpc7K1aArKZp3RsHtzRMKykeK9GVgn4WQGPR/go-ipfs-config"
36
	cid "gx/ipfs/QmPSQnBKM9g7BaUcZCvswUJVscQ1ipjmwxN5PXCjkp9EQ7/go-cid"
37
	u "gx/ipfs/QmPdKqUcHGFdeSpvjVoaTRPPstGif9GBZb5Q56RVw9o69A/go-ipfs-util"
Steven Allen's avatar
Steven Allen committed
38
	ic "gx/ipfs/QmPvyPwuCgJ7pDmrKDxRtsScJgBaM5h4EpRL2qQJsmXf4n/go-libp2p-crypto"
Steven Allen's avatar
Steven Allen committed
39 40
	dht "gx/ipfs/QmQHnqaNULV8WeUGgh97o9K3KAW6kWQmDyNf9UuikgnPTe/go-libp2p-kad-dht"
	dhtopts "gx/ipfs/QmQHnqaNULV8WeUGgh97o9K3KAW6kWQmDyNf9UuikgnPTe/go-libp2p-kad-dht/opts"
41
	exchange "gx/ipfs/QmR1nncPsZR14A4hWr39mq8Lm7BGgS68bHVT9nop8NpWEM/go-ipfs-exchange-interface"
Hector Sanjuan's avatar
Hector Sanjuan committed
42
	ipld "gx/ipfs/QmR7TcHkR9nxkUorfi8XMTAMLUK7GiP64TWWBzY3aacc1o/go-ipld-format"
43
	goprocess "gx/ipfs/QmSF8fPo3jgVBAy8fpdjjYqgG87dkJgUprRBHRd2tmfgpP/goprocess"
Jeromy's avatar
Jeromy committed
44
	mamask "gx/ipfs/QmSMZwvs3n4GBikZ7hKzT17c3bk65FmyZo2JqtJ16swqCv/multiaddr-filter"
Steven Allen's avatar
Steven Allen committed
45 46 47 48
	ma "gx/ipfs/QmT4U94DnD8FRfqr21obWY32HLM5VExccPKMjQHofeYqr9/go-multiaddr"
	peer "gx/ipfs/QmTRhk7cgjUf2gfQ3p2M9KPECNZEW9XUrmHcFCgog4cPgB/go-libp2p-peer"
	connmgr "gx/ipfs/QmTSih5JrkhMH62dp1oGjEwcaC38dxXBgRwTbeQEL4mPcU/go-libp2p-connmgr"
	pstore "gx/ipfs/QmTTJcDL3gsnGDALjh2fDGg1onGRUdVgNL2hU2WEZcVrMX/go-libp2p-peerstore"
Steven Allen's avatar
Steven Allen committed
49
	resolver "gx/ipfs/QmUB3RFRDctDp1k73mDJydzWiKdiuNHfyuoRPQeU52rWWT/go-path/resolver"
Steven Allen's avatar
Steven Allen committed
50 51 52 53 54
	libp2p "gx/ipfs/QmUDTcnDp2WssbmiDLC6aYurUeyt7QeRakHUQMxA2mZ5iB/go-libp2p"
	discovery "gx/ipfs/QmUDTcnDp2WssbmiDLC6aYurUeyt7QeRakHUQMxA2mZ5iB/go-libp2p/p2p/discovery"
	p2pbhost "gx/ipfs/QmUDTcnDp2WssbmiDLC6aYurUeyt7QeRakHUQMxA2mZ5iB/go-libp2p/p2p/host/basic"
	rhost "gx/ipfs/QmUDTcnDp2WssbmiDLC6aYurUeyt7QeRakHUQMxA2mZ5iB/go-libp2p/p2p/host/routed"
	identify "gx/ipfs/QmUDTcnDp2WssbmiDLC6aYurUeyt7QeRakHUQMxA2mZ5iB/go-libp2p/p2p/protocol/identify"
Steven Allen's avatar
Steven Allen committed
55
	psrouter "gx/ipfs/QmUhJjacEW7gDGsvxzn8NqyfCWRksgTtYeaGvQmgYvrmWH/go-libp2p-pubsub-router"
Steven Allen's avatar
Steven Allen committed
56
	quic "gx/ipfs/QmVX7uSFmFLZRFsN9QNPDJf7Pmhuv4GdedrKYrt2xXm5ag/go-libp2p-quic-transport"
Steven Allen's avatar
Steven Allen committed
57
	circuit "gx/ipfs/QmVYDvJjiKb9iFEyHxx4i1TJSRBLkQhGb5Fc8XpmDuNCEA/go-libp2p-circuit"
Steven Allen's avatar
Steven Allen committed
58
	ifconnmgr "gx/ipfs/QmWRvjn5BHMLCGkf48Hk1LDc4W72RPA9H59AAVCXmn9esJ/go-libp2p-interface-connmgr"
Steven Allen's avatar
Steven Allen committed
59
	bserv "gx/ipfs/QmWfhv1D18DRSiSm73r4QGcByspzPtxxRTcmHW3axFXZo8/go-blockservice"
Steven Allen's avatar
Steven Allen committed
60
	rhelpers "gx/ipfs/QmX3syBjwRd12qJGaKbFBWFfrBinKsaTC43ry3PsgiXCLK/go-libp2p-routing-helpers"
Steven Allen's avatar
Steven Allen committed
61
	merkledag "gx/ipfs/QmXyuFW7at4r9dxRAbrPU9JpHW5aqngAFyxvyhvYjzxRKS/go-merkledag"
Steven Allen's avatar
Steven Allen committed
62
	pnet "gx/ipfs/QmY4Q5JC4vxLEi8EpVxJM4rcRryEVtH1zRKVTAm6BKV1pg/go-libp2p-pnet"
Steven Allen's avatar
Steven Allen committed
63
	pubsub "gx/ipfs/QmY4dowpPFCBsbaoaJc9mNWso64eDJsm32LJznwPNaAiJG/go-libp2p-pubsub"
Steven Allen's avatar
Steven Allen committed
64
	smux "gx/ipfs/QmY9JXR3FupnYAYJWK9aMr9bCpqWKcToQ1tz8DVGTrHpHw/go-stream-muxer"
Steven Allen's avatar
Steven Allen committed
65
	logging "gx/ipfs/QmZChCsSt8DctjceaL56Eibc29CVQq4dGKRXC5JRZ6Ppae/go-log"
Steven Allen's avatar
Steven Allen committed
66
	record "gx/ipfs/Qma9Eqp16mNHDX1EL73pcxhFfzbyXVcAYtaDd1xdmDRDtL/go-libp2p-record"
Steven Allen's avatar
Steven Allen committed
67
	ds "gx/ipfs/QmaRb5yNXKonhbkpNxNawoydk4N6es6b4fPj19sjEKsh5D/go-datastore"
Steven Allen's avatar
Steven Allen committed
68
	mplex "gx/ipfs/QmaveCPGVaKJU57tBErGCDjzLaqEMZkFygoiv4BhYwWUGc/go-smux-multiplex"
Steven Allen's avatar
Steven Allen committed
69
	mafilter "gx/ipfs/QmbuCmYjYK5GQo4zKrK2h3NVsyBYf81ZQXgiE69CLLGHgB/go-maddr-filter"
Steven Allen's avatar
Steven Allen committed
70
	mfs "gx/ipfs/QmbvE3cYaSEfHTUU4GN4tvrRtHWTnyLVppiRcDamx1HC8i/go-mfs"
Steven Allen's avatar
Steven Allen committed
71
	bstore "gx/ipfs/QmcDDgAXDbpDUpadCJKLr49KYR4HuL7T8Z1dZTHt6ixsoR/go-ipfs-blockstore"
Steven Allen's avatar
Steven Allen committed
72
	routing "gx/ipfs/QmcQ81jSyWCp1jpkQ8CMbtpXT3jK7Wg6ZtYmoyWFgBoF9c/go-libp2p-routing"
Steven Allen's avatar
Steven Allen committed
73
	ft "gx/ipfs/Qmcba8ak38WXFWuaLak5pfJPUDFxcSWbkycBNQfmarpuTv/go-unixfs"
Steven Allen's avatar
Steven Allen committed
74 75 76
	nilrouting "gx/ipfs/QmcjvUP25nLSwELgUeqWe854S3XVbtsntTr7kZxG63yKhe/go-ipfs-routing/none"
	offroute "gx/ipfs/QmcjvUP25nLSwELgUeqWe854S3XVbtsntTr7kZxG63yKhe/go-ipfs-routing/offline"
	p2phost "gx/ipfs/QmdJfsSbKSZnMkfZ1kpopiyB9i3Hd6cp8VKWZmtWPa7Moc/go-libp2p-host"
Steven Allen's avatar
Steven Allen committed
77
	yamux "gx/ipfs/Qmdps3CYh5htGQSrPvzg5PHouVexLmtpbuLCqc4vuej8PC/go-smux-yamux"
Steven Allen's avatar
Steven Allen committed
78
	metrics "gx/ipfs/QmeaTjsfPf6vQ3WU2BUdjakgvKUHpuv3Fjxvb75N5iksMx/go-libp2p-metrics"
Łukasz Magiera's avatar
Łukasz Magiera committed
79
)
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
80

Jeromy's avatar
Jeromy committed
81
const IpnsValidatorTag = "ipns"
82

83
const kReprovideFrequency = time.Hour * 12
84
const discoveryConnTimeout = time.Second * 30
Jeromy's avatar
Jeromy committed
85

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

88 89 90 91
type mode int

const (
	// zero value is not a valid mode, must be explicitly set
92
	localMode mode = iota
93 94 95 96
	offlineMode
	onlineMode
)

97
func init() {
98
	identify.ClientVersion = "go-ipfs/" + version.CurrentVersionNumber + "/" + version.CurrentCommit
99 100
}

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

104
	// Self
105
	Identity peer.ID // the local node's identity
106

107
	Repo repo.Repo
108 109

	// Local node
110 111 112 113
	Pinning         pin.Pinner // the pinning manager
	Mounts          Mounts     // current mount state, if any.
	PrivateKey      ic.PrivKey // the local node's private Key
	PNetFingerprint []byte     // fingerprint of private network
114 115

	// Services
116 117 118 119 120 121 122 123 124 125 126 127
	Peerstore       pstore.Peerstore     // storage for other Peer instances
	Blockstore      bstore.GCBlockstore  // the block store (lower level)
	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
	Blocks          bserv.BlockService   // the block service, get/add blocks.
	DAG             ipld.DAGService      // the merkle dag service, get/add objects.
	Resolver        *resolver.Resolver   // the path resolution system
	Reporter        metrics.Reporter
	Discovery       discovery.Service
	FilesRoot       *mfs.Root
	RecordValidator record.Validator
128 129

	// Online
130 131 132 133 134
	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
135
	Reprovider   *rp.Reprovider      // the value reprovider system
Jeromy's avatar
Jeromy committed
136
	IpnsRepub    *ipnsrp.Republisher
137

Steven Allen's avatar
Steven Allen committed
138
	PubSub   *pubsub.PubSub
139
	PSRouter *psrouter.PubsubValueStore
140
	DHT      *dht.IpfsDHT
Łukasz Magiera's avatar
Łukasz Magiera committed
141
	P2P      *p2p.P2P
Jeromy's avatar
Jeromy committed
142

143 144
	proc goprocess.Process
	ctx  context.Context
145

Jeromy's avatar
Jeromy committed
146
	mode         mode
147
	localModeSet bool
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
148 149
}

150 151 152 153 154 155 156 157
// 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
}

vyzo's avatar
vyzo committed
158
func (n *IpfsNode) startOnlineServices(ctx context.Context, routingOption RoutingOption, hostOption HostOption, do DiscoveryOption, pubsub, ipnsps, mplex bool) error {
159
	if n.PeerHost != nil { // already online.
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
160
		return errors.New("node already online")
161 162 163
	}

	// load private key
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
164
	if err := n.LoadPrivateKey(); err != nil {
165 166 167
		return err
	}

Jeromy's avatar
Jeromy committed
168
	// get undialable addrs from config
169 170 171 172
	cfg, err := n.Repo.Config()
	if err != nil {
		return err
	}
Steven Allen's avatar
Steven Allen committed
173 174

	var libp2pOpts []libp2p.Option
175
	for _, s := range cfg.Swarm.AddrFilters {
Jeromy's avatar
Jeromy committed
176 177
		f, err := mamask.NewMask(s)
		if err != nil {
178
			return fmt.Errorf("incorrectly formatted address filter in config: %s", s)
Jeromy's avatar
Jeromy committed
179
		}
Steven Allen's avatar
Steven Allen committed
180
		libp2pOpts = append(libp2pOpts, libp2p.FilterAddresses(f))
Jeromy's avatar
Jeromy committed
181 182
	}

183 184 185
	if !cfg.Swarm.DisableBandwidthMetrics {
		// Set reporter
		n.Reporter = metrics.NewBandwidthCounter()
Steven Allen's avatar
Steven Allen committed
186
		libp2pOpts = append(libp2pOpts, libp2p.BandwidthReporter(n.Reporter))
187 188
	}

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

	if swarmkey != nil {
Steven Allen's avatar
Steven Allen committed
195
		protec, err := pnet.NewProtector(bytes.NewReader(swarmkey))
Jakub Sztandera's avatar
Jakub Sztandera committed
196
		if err != nil {
197
			return fmt.Errorf("failed to configure private network: %s", err)
Jakub Sztandera's avatar
Jakub Sztandera committed
198
		}
199
		n.PNetFingerprint = protec.Fingerprint()
200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217
		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
				}
			}
		}()
Steven Allen's avatar
Steven Allen committed
218 219

		libp2pOpts = append(libp2pOpts, libp2p.PrivateNetwork(protec))
Jakub Sztandera's avatar
Jakub Sztandera committed
220 221
	}

222 223 224 225
	addrsFactory, err := makeAddrsFactory(cfg.Addresses)
	if err != nil {
		return err
	}
Steven Allen's avatar
Steven Allen committed
226 227 228 229
	if !cfg.Swarm.DisableRelay {
		addrsFactory = composeAddrsFactory(addrsFactory, filterRelayAddrs)
	}
	libp2pOpts = append(libp2pOpts, libp2p.AddrsFactory(addrsFactory))
230

Steven Allen's avatar
Steven Allen committed
231
	connm, err := constructConnMgr(cfg.Swarm.ConnMgr)
Jeromy's avatar
Jeromy committed
232 233 234
	if err != nil {
		return err
	}
Steven Allen's avatar
Steven Allen committed
235 236 237
	libp2pOpts = append(libp2pOpts, libp2p.ConnectionManager(connm))

	libp2pOpts = append(libp2pOpts, makeSmuxTransportOption(mplex))
Jeromy's avatar
Jeromy committed
238

Steven Allen's avatar
Steven Allen committed
239 240
	if !cfg.Swarm.DisableNatPortMap {
		libp2pOpts = append(libp2pOpts, libp2p.NATPortMap())
241
	}
Steven Allen's avatar
Steven Allen committed
242

243 244 245
	// disable the default listen addrs
	libp2pOpts = append(libp2pOpts, libp2p.NoListenAddrs)

246 247 248 249 250 251 252 253 254 255 256
	if cfg.Swarm.DisableRelay {
		// Enabled by default.
		libp2pOpts = append(libp2pOpts, libp2p.DisableRelay())
	} else {
		relayOpts := []circuit.RelayOpt{circuit.OptDiscovery}
		if cfg.Swarm.EnableRelayHop {
			relayOpts = append(relayOpts, circuit.OptHop)
		}
		libp2pOpts = append(libp2pOpts, libp2p.EnableRelay(relayOpts...))
	}

257 258 259
	// explicitly enable the default transports
	libp2pOpts = append(libp2pOpts, libp2p.DefaultTransports)

Marten Seemann's avatar
Marten Seemann committed
260 261 262 263
	if cfg.Experimental.QUIC {
		libp2pOpts = append(libp2pOpts, libp2p.Transport(quic.NewTransport))
	}

Steven Allen's avatar
Steven Allen committed
264
	peerhost, err := hostOption(ctx, n.Identity, n.Peerstore, libp2pOpts...)
vyzo's avatar
vyzo committed
265

266
	if err != nil {
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
267
		return err
268 269
	}

270
	if err := n.startOnlineServicesWithHost(ctx, peerhost, routingOption, pubsub, ipnsps); err != nil {
271
		return err
272 273 274
	}

	// Ok, now we're ready to listen.
Łukasz Magiera's avatar
Łukasz Magiera committed
275
	if err := startListening(n.PeerHost, cfg); err != nil {
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
276
		return err
277
	}
278

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

281
	// setup local discovery
Jeromy's avatar
Jeromy committed
282
	if do != nil {
Jeromy's avatar
Jeromy committed
283
		service, err := do(ctx, n.PeerHost)
Jeromy's avatar
Jeromy committed
284
		if err != nil {
Jeromy's avatar
Jeromy committed
285 286 287 288
			log.Error("mdns error: ", err)
		} else {
			service.RegisterNotifee(n)
			n.Discovery = service
Jeromy's avatar
Jeromy committed
289
		}
290 291
	}

292
	return n.Bootstrap(DefaultBootstrapConfig)
293 294
}

Jeromy's avatar
Jeromy committed
295 296
func constructConnMgr(cfg config.ConnMgr) (ifconnmgr.ConnManager, error) {
	switch cfg.Type {
297 298 299 300
	case "":
		// 'default' value is the basic connection manager
		return connmgr.NewConnManager(config.DefaultConnMgrLowWater, config.DefaultConnMgrHighWater, config.DefaultConnMgrGracePeriod), nil
	case "none":
Jeromy's avatar
Jeromy committed
301 302 303 304 305 306 307 308 309 310 311 312 313
		return nil, nil
	case "basic":
		grace, err := time.ParseDuration(cfg.GracePeriod)
		if err != nil {
			return nil, fmt.Errorf("parsing Swarm.ConnMgr.GracePeriod: %s", err)
		}

		return connmgr.NewConnManager(cfg.LowWater, cfg.HighWater, grace), nil
	default:
		return nil, fmt.Errorf("unrecognized ConnMgr.Type: %q", cfg.Type)
	}
}

Łukasz Magiera's avatar
Łukasz Magiera committed
314 315 316 317 318 319
func (n *IpfsNode) startLateOnlineServices(ctx context.Context) error {
	cfg, err := n.Repo.Config()
	if err != nil {
		return err
	}

320
	var keyProvider rp.KeyChanFunc
Łukasz Magiera's avatar
Łukasz Magiera committed
321 322 323 324 325 326 327 328 329 330 331

	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:
332
		return fmt.Errorf("unknown reprovider strategy '%s'", cfg.Reprovider.Strategy)
Łukasz Magiera's avatar
Łukasz Magiera committed
333
	}
334
	n.Reprovider = rp.NewReprovider(ctx, n.Routing, keyProvider)
Łukasz Magiera's avatar
Łukasz Magiera committed
335

336 337 338 339 340
	reproviderInterval := kReprovideFrequency
	if cfg.Reprovider.Interval != "" {
		dur, err := time.ParseDuration(cfg.Reprovider.Interval)
		if err != nil {
			return err
Łukasz Magiera's avatar
Łukasz Magiera committed
341 342
		}

343
		reproviderInterval = dur
Łukasz Magiera's avatar
Łukasz Magiera committed
344 345
	}

346 347
	go n.Reprovider.Run(reproviderInterval)

Łukasz Magiera's avatar
Łukasz Magiera committed
348 349 350
	return nil
}

351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396
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
}

Steven Allen's avatar
Steven Allen committed
397 398 399
func makeSmuxTransportOption(mplexExp bool) libp2p.Option {
	const yamuxID = "/yamux/1.0.0"
	const mplexID = "/mplex/6.7.0"
400 401

	ymxtpt := &yamux.Transport{
Steven Allen's avatar
Steven Allen committed
402
		AcceptBacklog:          512,
403 404 405 406 407 408 409
		ConnectionWriteTimeout: time.Second * 10,
		KeepAliveInterval:      time.Second * 30,
		EnableKeepAlive:        true,
		MaxStreamWindowSize:    uint32(1024 * 512),
		LogOutput:              ioutil.Discard,
	}

410 411 412 413
	if os.Getenv("YAMUX_DEBUG") != "" {
		ymxtpt.LogOutput = os.Stderr
	}

Steven Allen's avatar
Steven Allen committed
414
	muxers := map[string]smux.Transport{yamuxID: ymxtpt}
415
	if mplexExp {
Steven Allen's avatar
Steven Allen committed
416
		muxers[mplexID] = mplex.DefaultTransport
417 418 419
	}

	// Allow muxer preference order overriding
Steven Allen's avatar
Steven Allen committed
420
	order := []string{yamuxID, mplexID}
421
	if prefs := os.Getenv("LIBP2P_MUX_PREFS"); prefs != "" {
Steven Allen's avatar
Steven Allen committed
422 423 424 425 426 427 428 429 430 431 432 433
		order = strings.Fields(prefs)
	}

	opts := make([]libp2p.Option, 0, len(order))
	for _, id := range order {
		tpt, ok := muxers[id]
		if !ok {
			log.Warning("unknown or duplicate muxer in LIBP2P_MUX_PREFS: %s", id)
			continue
		}
		delete(muxers, id)
		opts = append(opts, libp2p.Muxer(id, tpt))
434 435
	}

Steven Allen's avatar
Steven Allen committed
436
	return libp2p.ChainOptions(opts...)
437 438
}

Jeromy's avatar
Jeromy committed
439 440
func setupDiscoveryOption(d config.Discovery) DiscoveryOption {
	if d.MDNS.Enabled {
Jeromy's avatar
Jeromy committed
441
		return func(ctx context.Context, h p2phost.Host) (discovery.Service, error) {
Jeromy's avatar
Jeromy committed
442 443 444
			if d.MDNS.Interval == 0 {
				d.MDNS.Interval = 5
			}
Jeromy's avatar
Jeromy committed
445
			return discovery.NewMdnsService(ctx, h, time.Duration(d.MDNS.Interval)*time.Second, discovery.ServiceTag)
Jeromy's avatar
Jeromy committed
446 447 448 449 450
		}
	}
	return nil
}

451 452
// HandlePeerFound attempts to connect to peer from `PeerInfo`, if it fails
// logs a warning log.
Jeromy's avatar
Jeromy committed
453
func (n *IpfsNode) HandlePeerFound(p pstore.PeerInfo) {
454
	log.Warning("trying peer info: ", p)
455
	ctx, cancel := context.WithTimeout(n.Context(), discoveryConnTimeout)
rht's avatar
rht committed
456
	defer cancel()
457
	if err := n.PeerHost.Connect(ctx, p); err != nil {
458 459 460 461
		log.Warning("Failed to connect to peer found by discovery: ", err)
	}
}

462 463
// startOnlineServicesWithHost  is the set of services which need to be
// initialized with the host and _before_ we start listening.
Steven Allen's avatar
Steven Allen committed
464 465
func (n *IpfsNode) startOnlineServicesWithHost(ctx context.Context, host p2phost.Host, routingOption RoutingOption, enablePubsub bool, enableIpnsps bool) error {
	if enablePubsub || enableIpnsps {
466 467 468 469 470
		cfg, err := n.Repo.Config()
		if err != nil {
			return err
		}

Steven Allen's avatar
Steven Allen committed
471
		var service *pubsub.PubSub
472

Steven Allen's avatar
Steven Allen committed
473 474 475 476 477 478 479 480 481
		var pubsubOptions []pubsub.Option
		if cfg.Pubsub.DisableSigning {
			pubsubOptions = append(pubsubOptions, pubsub.WithMessageSigning(false))
		}

		if cfg.Pubsub.StrictSignatureVerification {
			pubsubOptions = append(pubsubOptions, pubsub.WithStrictSignatureVerification(true))
		}

482 483 484 485
		switch cfg.Pubsub.Router {
		case "":
			fallthrough
		case "floodsub":
Steven Allen's avatar
Steven Allen committed
486
			service, err = pubsub.NewFloodSub(ctx, host, pubsubOptions...)
487 488

		case "gossipsub":
Steven Allen's avatar
Steven Allen committed
489
			service, err = pubsub.NewGossipSub(ctx, host, pubsubOptions...)
490 491 492 493 494

		default:
			err = fmt.Errorf("Unknown pubsub router %s", cfg.Pubsub.Router)
		}

495 496 497
		if err != nil {
			return err
		}
Steven Allen's avatar
Steven Allen committed
498
		n.PubSub = service
499 500
	}

501
	// setup routing service
502
	r, err := routingOption(ctx, host, n.Repo.Datastore(), n.RecordValidator)
Jeromy's avatar
Jeromy committed
503
	if err != nil {
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
504
		return err
505
	}
Jeromy's avatar
Jeromy committed
506
	n.Routing = r
507

508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524
	// TODO: I'm not a fan of type assertions like this but the
	// `RoutingOption` system doesn't currently provide access to the
	// IpfsNode.
	//
	// Ideally, we'd do something like:
	//
	// 1. Add some fancy method to introspect into tiered routers to extract
	//    things like the pubsub router or the DHT (complicated, messy,
	//    probably not worth it).
	// 2. Pass the IpfsNode into the RoutingOption (would also remove the
	//    PSRouter case below.
	// 3. Introduce some kind of service manager? (my personal favorite but
	//    that requires a fair amount of work).
	if dht, ok := r.(*dht.IpfsDHT); ok {
		n.DHT = dht
	}

Steven Allen's avatar
Steven Allen committed
525
	if enableIpnsps {
526 527 528 529
		n.PSRouter = psrouter.NewPubsubValueStore(
			ctx,
			host,
			n.Routing,
Steven Allen's avatar
Steven Allen committed
530
			n.PubSub,
531
			n.RecordValidator,
532 533
		)
		n.Routing = rhelpers.Tiered{
Łukasz Magiera's avatar
Łukasz Magiera committed
534 535 536 537 538 539 540
			Routers: []routing.IpfsRouting{
				// Always check pubsub first.
				&rhelpers.Compose{
					ValueStore: &rhelpers.LimitedValueStore{
						ValueStore: n.PSRouter,
						Namespaces: []string{"ipns"},
					},
541
				},
Łukasz Magiera's avatar
Łukasz Magiera committed
542
				n.Routing,
543
			},
Łukasz Magiera's avatar
Łukasz Magiera committed
544
			Validator: n.RecordValidator,
545 546 547
		}
	}

548 549 550
	// Wrap standard peer host with routing system to allow unknown peer lookups
	n.PeerHost = rhost.Wrap(host, n.Routing)

551
	// setup exchange service
552
	bitswapNetwork := bsnet.NewFromIpfsHost(n.PeerHost, n.Routing)
Łukasz Magiera's avatar
Łukasz Magiera committed
553
	n.Exchange = bitswap.New(ctx, bitswapNetwork, n.Blockstore)
554

555 556 557 558 559
	size, err := n.getCacheSize()
	if err != nil {
		return err
	}

560
	// setup name system
561
	n.Namesys = namesys.NewNameSystem(n.Routing, n.Repo.Datastore(), size)
562

Jeromy's avatar
Jeromy committed
563
	// setup ipns republishing
564
	return n.setupIpnsRepublisher()
565 566
}

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

584
func (n *IpfsNode) setupIpnsRepublisher() error {
Jeromy's avatar
Jeromy committed
585 586 587 588
	cfg, err := n.Repo.Config()
	if err != nil {
		return err
	}
589

590
	n.IpnsRepub = ipnsrp.NewRepublisher(n.Namesys, n.Repo.Datastore(), n.PrivateKey, n.Repo.Keystore())
591

Jeromy's avatar
Jeromy committed
592 593 594 595 596 597
	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)
		}

598
		if !u.Debug && (d < time.Minute || d > (time.Hour*24)) {
Jeromy's avatar
Jeromy committed
599 600 601 602 603 604
			return fmt.Errorf("config setting IPNS.RepublishPeriod is not between 1min and 1day: %s", d)
		}

		n.IpnsRepub.Interval = d
	}

605
	if cfg.Ipns.RecordLifetime != "" {
606
		d, err := time.ParseDuration(cfg.Ipns.RecordLifetime)
607 608 609 610 611 612 613
		if err != nil {
			return fmt.Errorf("failure to parse config setting IPNS.RecordLifetime: %s", err)
		}

		n.IpnsRepub.RecordLifetime = d
	}

Jeromy's avatar
Jeromy committed
614 615
	n.Process().Go(n.IpnsRepub.Run)

616 617 618
	return nil
}

619 620 621 622 623 624 625 626 627 628 629 630
// 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 {
631 632 633
	if n.ctx == nil {
		n.ctx = context.TODO()
	}
634 635 636
	return n.ctx
}

637 638
// teardown closes owned children. If any errors occur, this function returns
// the first error.
Brian Tiger Chow's avatar
Brian Tiger Chow committed
639
func (n *IpfsNode) teardown() error {
640
	log.Debug("core is shutting down...")
641 642
	// 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
643 644
	var closers []io.Closer

645
	// NOTE: The order that objects are added(closed) matters, if an object
Jeromy's avatar
Jeromy committed
646 647 648 649 650
	// 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
651
	}
652

653 654 655 656
	if n.Exchange != nil {
		closers = append(closers, n.Exchange)
	}

657
	if n.Mounts.Ipfs != nil && !n.Mounts.Ipfs.IsActive() {
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
658 659
		closers = append(closers, mount.Closer(n.Mounts.Ipfs))
	}
660
	if n.Mounts.Ipns != nil && !n.Mounts.Ipns.IsActive() {
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
661 662 663
		closers = append(closers, mount.Closer(n.Mounts.Ipns))
	}

664 665
	if n.DHT != nil {
		closers = append(closers, n.DHT.Process())
Jeromy's avatar
Jeromy committed
666 667
	}

Jeromy's avatar
Jeromy committed
668 669 670 671
	if n.Blocks != nil {
		closers = append(closers, n.Blocks)
	}

Jeromy's avatar
Jeromy committed
672 673
	if n.Bootstrapper != nil {
		closers = append(closers, n.Bootstrapper)
674 675
	}

Jeromy's avatar
Jeromy committed
676 677
	if n.PeerHost != nil {
		closers = append(closers, n.PeerHost)
678
	}
679

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

683
	var errs []error
684
	for _, closer := range closers {
685 686
		if err := closer.Close(); err != nil {
			errs = append(errs, err)
687 688 689 690
		}
	}
	if len(errs) > 0 {
		return errs[0]
Brian Tiger Chow's avatar
Brian Tiger Chow committed
691 692
	}
	return nil
Brian Tiger Chow's avatar
Brian Tiger Chow committed
693 694
}

695
// OnlineMode returns whether or not the IpfsNode is in OnlineMode.
Brian Tiger Chow's avatar
Brian Tiger Chow committed
696
func (n *IpfsNode) OnlineMode() bool {
697
	return n.mode == onlineMode
Brian Tiger Chow's avatar
Brian Tiger Chow committed
698 699
}

700
// SetLocal will set the IpfsNode to local mode
701 702 703 704 705 706 707
func (n *IpfsNode) SetLocal(isLocal bool) {
	if isLocal {
		n.mode = localMode
	}
	n.localModeSet = true
}

708
// LocalMode returns whether or not the IpfsNode is in LocalMode
709 710 711 712 713
func (n *IpfsNode) LocalMode() bool {
	if !n.localModeSet {
		// programmer error should not happen
		panic("local mode not set")
	}
714
	return n.mode == localMode
715 716
}

717
// Bootstrap will set and call the IpfsNodes bootstrap function.
718
func (n *IpfsNode) Bootstrap(cfg BootstrapConfig) error {
719
	// TODO what should return value be when in offlineMode?
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
720 721 722 723
	if n.Routing == nil {
		return nil
	}

724 725 726 727 728 729 730
	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
731
		cfg.BootstrapPeers = func() []pstore.PeerInfo {
732
			ps, err := n.loadBootstrapPeers()
733
			if err != nil {
734
				log.Warning("failed to parse bootstrap peers from config")
735 736 737 738 739 740 741 742 743
				return nil
			}
			return ps
		}
	}

	var err error
	n.Bootstrapper, err = Bootstrap(n, cfg)
	return err
744 745
}

746 747
func (n *IpfsNode) loadID() error {
	if n.Identity != "" {
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
748
		return errors.New("identity already loaded")
749 750
	}

751 752 753 754 755 756
	cfg, err := n.Repo.Config()
	if err != nil {
		return err
	}

	cid := cfg.Identity.PeerID
757
	if cid == "" {
758
		return errors.New("identity was not set in config (was 'ipfs init' run?)")
759 760
	}
	if len(cid) == 0 {
761
		return errors.New("no peer ID in config! (was 'ipfs init' run?)")
762 763
	}

Steven Allen's avatar
Steven Allen committed
764 765 766 767 768 769
	id, err := peer.IDB58Decode(cid)
	if err != nil {
		return fmt.Errorf("peer ID invalid: %s", err)
	}

	n.Identity = id
770 771
	return nil
}
772

773
// GetKey will return a key from the Keystore with name `name`.
774 775 776 777 778 779 780 781
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
782
func (n *IpfsNode) LoadPrivateKey() error {
783
	if n.Identity == "" || n.Peerstore == nil {
Łukasz Magiera's avatar
Łukasz Magiera committed
784
		return errors.New("loaded private key out of order")
785 786
	}

787
	if n.PrivateKey != nil {
Kejie Zhang's avatar
Kejie Zhang committed
788 789
		log.Warning("private key already loaded")
		return nil
790 791
	}

792 793 794 795 796 797
	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
798
	if err != nil {
799
		return err
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
800
	}
801

802 803
	n.PrivateKey = sk
	n.Peerstore.AddPrivKey(n.Identity, n.PrivateKey)
Jeromy's avatar
Jeromy committed
804 805 806 807
	n.Peerstore.AddPubKey(n.Identity, sk.GetPublic())
	return nil
}

Jeromy's avatar
Jeromy committed
808
func (n *IpfsNode) loadBootstrapPeers() ([]pstore.PeerInfo, error) {
809 810 811 812 813 814
	cfg, err := n.Repo.Config()
	if err != nil {
		return nil, err
	}

	parsed, err := cfg.BootstrapPeers()
815 816 817 818 819 820
	if err != nil {
		return nil, err
	}
	return toPeerInfos(parsed), nil
}

Jeromy's avatar
Jeromy committed
821
func (n *IpfsNode) loadFilesRoot() error {
Jeromy's avatar
Jeromy committed
822
	dsk := ds.NewKey("/local/filesroot")
823
	pf := func(ctx context.Context, c cid.Cid) error {
Jeromy's avatar
Jeromy committed
824
		return n.Repo.Datastore().Put(dsk, c.Bytes())
Jeromy's avatar
Jeromy committed
825 826
	}

827
	var nd *merkledag.ProtoNode
Jeromy's avatar
Jeromy committed
828 829 830 831
	val, err := n.Repo.Datastore().Get(dsk)

	switch {
	case err == ds.ErrNotFound || val == nil:
832
		nd = ft.EmptyDirNode()
833
		err := n.DAG.Add(n.Context(), nd)
Jeromy's avatar
Jeromy committed
834 835 836 837
		if err != nil {
			return fmt.Errorf("failure writing to dagstore: %s", err)
		}
	case err == nil:
838
		c, err := cid.Cast(val)
Jeromy's avatar
Jeromy committed
839 840 841 842
		if err != nil {
			return err
		}

843
		rnd, err := n.DAG.Get(n.Context(), c)
Jeromy's avatar
Jeromy committed
844 845 846
		if err != nil {
			return fmt.Errorf("error loading filesroot from DAG: %s", err)
		}
847 848 849 850 851 852 853

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

		nd = pbnd
Jeromy's avatar
Jeromy committed
854 855 856 857 858 859 860 861 862 863 864 865 866
	default:
		return err
	}

	mr, err := mfs.NewRoot(n.Context(), n.DAG, nd, pf)
	if err != nil {
		return err
	}

	n.FilesRoot = mr
	return nil
}

867 868
// SetupOfflineRouting instantiates a routing system in offline mode. This is
// primarily used for offline ipns modifications.
Jeromy's avatar
Jeromy committed
869
func (n *IpfsNode) SetupOfflineRouting() error {
870 871 872 873
	if n.Routing != nil {
		// Routing was already set up
		return nil
	}
874 875

	// TODO: move this somewhere else.
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
876
	err := n.LoadPrivateKey()
Jeromy's avatar
Jeromy committed
877 878 879 880
	if err != nil {
		return err
	}

881
	n.Routing = offroute.NewOfflineRouter(n.Repo.Datastore(), n.RecordValidator)
882

883 884 885 886 887 888
	size, err := n.getCacheSize()
	if err != nil {
		return err
	}

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

890
	return nil
891 892 893 894
}

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

899 900 901 902
	id2, err := peer.IDFromPrivateKey(sk)
	if err != nil {
		return nil, err
	}
903

904 905
	if id2 != id {
		return nil, fmt.Errorf("private key in config does not match id: %s != %s", id, id2)
906 907
	}

908
	return sk, nil
909
}
910

911
func listenAddresses(cfg *config.Config) ([]ma.Multiaddr, error) {
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
912 913 914
	var listen []ma.Multiaddr
	for _, addr := range cfg.Addresses.Swarm {
		maddr, err := ma.NewMultiaddr(addr)
915
		if err != nil {
Łukasz Magiera's avatar
Łukasz Magiera committed
916
			return nil, fmt.Errorf("failure to parse config.Addresses.Swarm: %s", cfg.Addresses.Swarm)
917
		}
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
918
		listen = append(listen, maddr)
919 920 921 922
	}

	return listen, nil
}
923

Kevin Atkinson's avatar
Kevin Atkinson committed
924
type ConstructPeerHostOpts struct {
925
	AddrsFactory      p2pbhost.AddrsFactory
vyzo's avatar
vyzo committed
926 927 928
	DisableNatPortMap bool
	DisableRelay      bool
	EnableRelayHop    bool
Jeromy's avatar
Jeromy committed
929
	ConnectionManager ifconnmgr.ConnManager
Kevin Atkinson's avatar
Kevin Atkinson committed
930 931
}

Steven Allen's avatar
Steven Allen committed
932
type HostOption func(ctx context.Context, id peer.ID, ps pstore.Peerstore, options ...libp2p.Option) (p2phost.Host, error)
Jeromy's avatar
Jeromy committed
933 934 935

var DefaultHostOption HostOption = constructPeerHost

936
// isolates the complex initialization steps
Steven Allen's avatar
Steven Allen committed
937 938 939 940
func constructPeerHost(ctx context.Context, id peer.ID, ps pstore.Peerstore, options ...libp2p.Option) (p2phost.Host, error) {
	pkey := ps.PrivKey(id)
	if pkey == nil {
		return nil, fmt.Errorf("missing private key for node ID: %s", id.Pretty())
941
	}
Steven Allen's avatar
Steven Allen committed
942 943
	options = append([]libp2p.Option{libp2p.Identity(pkey), libp2p.Peerstore(ps)}, options...)
	return libp2p.New(ctx, options...)
944 945
}

946 947 948 949 950 951 952 953 954 955 956 957
func filterRelayAddrs(addrs []ma.Multiaddr) []ma.Multiaddr {
	var raddrs []ma.Multiaddr
	for _, addr := range addrs {
		_, err := addr.ValueForProtocol(circuit.P_CIRCUIT)
		if err == nil {
			continue
		}
		raddrs = append(raddrs, addr)
	}
	return raddrs
}

958 959 960 961 962 963
func composeAddrsFactory(f, g p2pbhost.AddrsFactory) p2pbhost.AddrsFactory {
	return func(addrs []ma.Multiaddr) []ma.Multiaddr {
		return f(g(addrs))
	}
}

964
// startListening on the network addresses
Łukasz Magiera's avatar
Łukasz Magiera committed
965
func startListening(host p2phost.Host, cfg *config.Config) error {
966 967
	listenAddrs, err := listenAddresses(cfg)
	if err != nil {
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
968
		return err
969 970 971
	}

	// Actually start listening:
Steven Allen's avatar
Steven Allen committed
972
	if err := host.Network().Listen(listenAddrs...); err != nil {
973
		return err
974 975
	}

976
	// list out our addresses
977
	addrs, err := host.Network().InterfaceListenAddresses()
978
	if err != nil {
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
979
		return err
980
	}
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
981
	log.Infof("Swarm listening at: %s", addrs)
982
	return nil
983
}
984

985 986 987 988 989 990
func constructDHTRouting(ctx context.Context, host p2phost.Host, dstore ds.Batching, validator record.Validator) (routing.IpfsRouting, error) {
	return dht.New(
		ctx, host,
		dhtopts.Datastore(dstore),
		dhtopts.Validator(validator),
	)
991
}
Jeromy's avatar
Jeromy committed
992

993 994 995 996 997 998 999
func constructClientDHTRouting(ctx context.Context, host p2phost.Host, dstore ds.Batching, validator record.Validator) (routing.IpfsRouting, error) {
	return dht.New(
		ctx, host,
		dhtopts.Client(true),
		dhtopts.Datastore(dstore),
		dhtopts.Validator(validator),
	)
Jeromy's avatar
Jeromy committed
1000 1001
}

1002
type RoutingOption func(context.Context, p2phost.Host, ds.Batching, record.Validator) (routing.IpfsRouting, error)
Jeromy's avatar
Jeromy committed
1003

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

1006
var DHTOption RoutingOption = constructDHTRouting
Jeromy's avatar
Jeromy committed
1007
var DHTClientOption RoutingOption = constructClientDHTRouting
1008
var NilRouterOption RoutingOption = nilrouting.ConstructNilRouting