core.go 9.23 KB
Newer Older
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
1 2
package core

Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
3
import (
4
	"errors"
Juan Batiz-Benet's avatar
go fmt  
Juan Batiz-Benet committed
5
	"fmt"
6

7
	context "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/go.net/context"
8
	b58 "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-base58"
9
	ctxgroup "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-ctxgroup"
10
	ma "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multiaddr"
11

12
	bstore "github.com/jbenet/go-ipfs/blocks/blockstore"
13
	bserv "github.com/jbenet/go-ipfs/blockservice"
Juan Batiz-Benet's avatar
go fmt  
Juan Batiz-Benet committed
14
	config "github.com/jbenet/go-ipfs/config"
Jeromy's avatar
Jeromy committed
15
	diag "github.com/jbenet/go-ipfs/diagnostics"
16 17
	exchange "github.com/jbenet/go-ipfs/exchange"
	bitswap "github.com/jbenet/go-ipfs/exchange/bitswap"
18
	bsnet "github.com/jbenet/go-ipfs/exchange/bitswap/network"
19
	"github.com/jbenet/go-ipfs/exchange/offline"
20
	mount "github.com/jbenet/go-ipfs/fuse/mount"
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
21
	merkledag "github.com/jbenet/go-ipfs/merkledag"
Jeromy's avatar
Jeromy committed
22
	namesys "github.com/jbenet/go-ipfs/namesys"
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
23
	ic "github.com/jbenet/go-ipfs/p2p/crypto"
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
24 25 26
	p2phost "github.com/jbenet/go-ipfs/p2p/host"
	p2pbhost "github.com/jbenet/go-ipfs/p2p/host/basic"
	swarm "github.com/jbenet/go-ipfs/p2p/net/swarm"
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
27
	peer "github.com/jbenet/go-ipfs/p2p/peer"
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
28
	path "github.com/jbenet/go-ipfs/path"
Jeromy's avatar
Jeromy committed
29
	pin "github.com/jbenet/go-ipfs/pin"
30 31
	routing "github.com/jbenet/go-ipfs/routing"
	dht "github.com/jbenet/go-ipfs/routing/dht"
32
	ds2 "github.com/jbenet/go-ipfs/util/datastore2"
33
	debugerror "github.com/jbenet/go-ipfs/util/debugerror"
34
	eventlog "github.com/jbenet/go-ipfs/util/eventlog"
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
35 36
)

Jeromy's avatar
Jeromy committed
37
const IpnsValidatorTag = "ipns"
38
const kSizeBlockstoreWriteCache = 100
Jeromy's avatar
Jeromy committed
39

Brian Tiger Chow's avatar
Brian Tiger Chow committed
40
var log = eventlog.Logger("core")
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
41

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

45 46
	// Self
	Config     *config.Config // the node's configuration
47 48
	Identity   peer.ID        // the local node's identity
	PrivateKey ic.PrivKey     // the local node's private Key
49 50 51 52 53 54 55 56 57
	onlineMode bool           // alternatively, offline

	// Local node
	Datastore ds2.ThreadSafeDatastoreCloser // the local datastore
	Pinning   pin.Pinner                    // the pinning manager
	Mounts    Mounts                        // current mount state, if any.

	// Services
	Peerstore   peer.Peerstore       // storage for other Peer instances
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
58
	PeerHost    p2phost.Host         // the network host (server+client)
59 60
	Routing     routing.IpfsRouting  // the routing system. recommend ipfs-dht
	Exchange    exchange.Interface   // the block exchange + strategy (bitswap)
61
	Blockstore  bstore.Blockstore    // the block store (lower level)
62 63 64 65 66
	Blocks      *bserv.BlockService  // the block service, get/add blocks.
	DAG         merkledag.DAGService // the merkle dag service, get/add objects.
	Resolver    *path.Resolver       // the path resolution system
	Namesys     namesys.NameSystem   // the name system, resolves paths to hashes
	Diagnostics *diag.Diagnostics    // the diagnostics service
67

68
	ctxgroup.ContextGroup
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
69 70
}

71 72 73 74 75 76 77 78
// 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
}

79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107
var errTODO = errors.New("TODO")

type Configuration *IpfsNode // define a different type

type ConfigOption func(ctx context.Context) (Configuration, error)

func NewIPFSNode(ctx context.Context, option ConfigOption) (*IpfsNode, error) {
	config, err := option(ctx)
	if err != nil {
		return nil, err
	}
	return config, nil
}

func Offline(cfg *config.Config) ConfigOption {
	return Standard(cfg, false)
}

func Online(cfg *config.Config) ConfigOption {
	return Standard(cfg, true)
}

// DEPRECATED: use Online, Offline functions
func Standard(cfg *config.Config, online bool) ConfigOption {
	return func(ctx context.Context) (Configuration, error) {
		return NewIpfsNode(ctx, cfg, online)
	}
}

Juan Batiz-Benet's avatar
go lint  
Juan Batiz-Benet committed
108
// NewIpfsNode constructs a new IpfsNode based on the given config.
109
// DEPRECATED: use `NewIPFSNode`
110
func NewIpfsNode(ctx context.Context, cfg *config.Config, online bool) (n *IpfsNode, err error) {
111 112
	success := false // flip to true after all sub-system inits succeed
	defer func() {
113 114
		if !success && n != nil {
			n.Close()
115 116
		}
	}()
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
117

Juan Batiz-Benet's avatar
go fmt  
Juan Batiz-Benet committed
118
	if cfg == nil {
Brian Tiger Chow's avatar
Brian Tiger Chow committed
119
		return nil, debugerror.Errorf("configuration required")
Juan Batiz-Benet's avatar
go fmt  
Juan Batiz-Benet committed
120 121
	}

122
	n = &IpfsNode{
Brian Tiger Chow's avatar
Brian Tiger Chow committed
123 124
		onlineMode: online,
		Config:     cfg,
125
	}
126 127
	n.ContextGroup = ctxgroup.WithContextAndTeardown(ctx, n.teardown)
	ctx = n.ContextGroup.Context()
128

129 130 131
	// setup Peerstore
	n.Peerstore = peer.NewPeerstore()

132 133
	// setup datastore.
	if n.Datastore, err = makeDatastore(cfg.Datastore); err != nil {
Brian Tiger Chow's avatar
Brian Tiger Chow committed
134
		return nil, debugerror.Wrap(err)
Juan Batiz-Benet's avatar
go fmt  
Juan Batiz-Benet committed
135 136
	}

137 138 139
	// setup local peer ID (private key is loaded in online setup)
	if err := n.loadID(); err != nil {
		return nil, err
140 141
	}

142 143 144
	n.Blockstore, err = bstore.WriteCached(bstore.NewBlockstore(n.Datastore), kSizeBlockstoreWriteCache)
	if err != nil {
		return nil, debugerror.Wrap(err)
145 146
	}

147
	// setup online services
148
	if online {
149 150
		if err := n.StartOnlineServices(); err != nil {
			return nil, err // debugerror.Wraps.
151
		}
152 153
	} else {
		n.Exchange = offline.Exchange(n.Blockstore)
154
	}
155

156
	n.Blocks, err = bserv.New(n.Blockstore, n.Exchange)
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
157
	if err != nil {
Brian Tiger Chow's avatar
Brian Tiger Chow committed
158
		return nil, debugerror.Wrap(err)
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
159 160
	}

161 162
	n.DAG = merkledag.NewDAGService(n.Blocks)
	n.Pinning, err = pin.LoadPinner(n.Datastore, n.DAG)
Jeromy's avatar
Jeromy committed
163
	if err != nil {
164
		n.Pinning = pin.NewPinner(n.Datastore, n.DAG)
Jeromy's avatar
Jeromy committed
165
	}
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
166 167
	n.Resolver = &path.Resolver{DAG: n.DAG}

168
	success = true
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
169
	return n, nil
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
170
}
171

172 173 174 175 176 177 178 179 180 181 182 183
func (n *IpfsNode) StartOnlineServices() error {
	ctx := n.Context()

	if n.PeerHost != nil { // already online.
		return debugerror.New("node already online")
	}

	// load private key
	if err := n.loadPrivateKey(); err != nil {
		return err
	}

184 185
	if err := n.startNetwork(); err != nil {
		return err
186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215
	}

	// setup diagnostics service
	n.Diagnostics = diag.NewDiagnostics(n.Identity, n.PeerHost)

	// setup routing service
	dhtRouting := dht.NewDHT(ctx, n.PeerHost, n.Datastore)
	dhtRouting.Validators[IpnsValidatorTag] = namesys.ValidateIpnsRecord
	n.Routing = dhtRouting
	n.AddChildGroup(dhtRouting)

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

	// setup name system
	// TODO implement an offline namesys that serves only local names.
	n.Namesys = namesys.NewNameSystem(n.Routing)

	// TODO consider moving connection supervision into the Network. We've
	// discussed improvements to this Node constructor. One improvement
	// would be to make the node configurable, allowing clients to inject
	// an Exchange, Network, or Routing component and have the constructor
	// manage the wiring. In that scenario, this dangling function is a bit
	// awkward.
	go superviseConnections(ctx, n.PeerHost, dhtRouting, n.Peerstore, n.Config.Bootstrap)
	return nil
}

216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243
func (n *IpfsNode) startNetwork() error {
	ctx := n.Context()

	// setup the network
	listenAddrs, err := listenAddresses(n.Config)
	if err != nil {
		return debugerror.Wrap(err)
	}
	// make sure we dont error out if our config includes some addresses we cant use.
	listenAddrs = swarm.FilterAddrs(listenAddrs)
	network, err := swarm.NewNetwork(ctx, listenAddrs, n.Identity, n.Peerstore)
	if err != nil {
		return debugerror.Wrap(err)
	}
	n.AddChildGroup(network.CtxGroup())
	n.PeerHost = p2pbhost.New(network)

	// explicitly set these as our listen addrs.
	// (why not do it inside inet.NewNetwork? because this way we can
	// listen on addresses without necessarily advertising those publicly.)
	addrs, err := n.PeerHost.Network().InterfaceListenAddresses()
	if err != nil {
		return debugerror.Wrap(err)
	}
	n.Peerstore.AddAddresses(n.Identity, addrs)
	return nil
}

Brian Tiger Chow's avatar
Brian Tiger Chow committed
244 245 246 247 248
func (n *IpfsNode) teardown() error {
	if err := n.Datastore.Close(); err != nil {
		return err
	}
	return nil
Brian Tiger Chow's avatar
Brian Tiger Chow committed
249 250
}

Brian Tiger Chow's avatar
Brian Tiger Chow committed
251 252
func (n *IpfsNode) OnlineMode() bool {
	return n.onlineMode
Brian Tiger Chow's avatar
Brian Tiger Chow committed
253 254
}

255 256 257
func (n *IpfsNode) loadID() error {
	if n.Identity != "" {
		return debugerror.New("identity already loaded")
258 259
	}

260 261 262 263 264 265
	cid := n.Config.Identity.PeerID
	if cid == "" {
		return debugerror.New("Identity was not set in config (was ipfs init run?)")
	}
	if len(cid) == 0 {
		return debugerror.New("No peer ID in config! (was ipfs init run?)")
266 267
	}

268 269 270
	n.Identity = peer.ID(b58.Decode(cid))
	return nil
}
271

272 273 274
func (n *IpfsNode) loadPrivateKey() error {
	if n.Identity == "" || n.Peerstore == nil {
		return debugerror.New("loaded private key out of order.")
275 276
	}

277 278 279 280 281
	if n.PrivateKey != nil {
		return debugerror.New("private key already loaded")
	}

	sk, err := loadPrivateKey(&n.Config.Identity, n.Identity)
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
282
	if err != nil {
283
		return err
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
284
	}
285

286 287 288
	n.PrivateKey = sk
	n.Peerstore.AddPrivKey(n.Identity, n.PrivateKey)
	return nil
289 290 291 292
}

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

297 298 299 300
	id2, err := peer.IDFromPrivateKey(sk)
	if err != nil {
		return nil, err
	}
301

302 303
	if id2 != id {
		return nil, fmt.Errorf("private key in config does not match id: %s != %s", id, id2)
304 305
	}

306
	return sk, nil
307
}
308

309 310
func listenAddresses(cfg *config.Config) ([]ma.Multiaddr, error) {

311 312 313 314 315
	var err error
	listen := make([]ma.Multiaddr, len(cfg.Addresses.Swarm))
	for i, addr := range cfg.Addresses.Swarm {

		listen[i], err = ma.NewMultiaddr(addr)
316
		if err != nil {
317
			return nil, fmt.Errorf("Failure to parse config.Addresses.Swarm[%d]: %s", i, cfg.Addresses.Swarm)
318 319 320 321 322
		}
	}

	return listen, nil
}