core.go 8.25 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 (
Juan Batiz-Benet's avatar
go fmt  
Juan Batiz-Benet committed
4
	"fmt"
5

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

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

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

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

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

44 45
	// Self
	Config     *config.Config // the node's configuration
46 47
	Identity   peer.ID        // the local node's identity
	PrivateKey ic.PrivKey     // the local node's private Key
48 49 50 51 52 53 54 55 56
	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
57
	PeerHost    p2phost.Host         // the network host (server+client)
58 59
	Routing     routing.IpfsRouting  // the routing system. recommend ipfs-dht
	Exchange    exchange.Interface   // the block exchange + strategy (bitswap)
60
	Blockstore  bstore.Blockstore    // the block store (lower level)
61 62 63 64 65
	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
66

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

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

Juan Batiz-Benet's avatar
go lint  
Juan Batiz-Benet committed
78
// NewIpfsNode constructs a new IpfsNode based on the given config.
79
func NewIpfsNode(ctx context.Context, cfg *config.Config, online bool) (n *IpfsNode, err error) {
80 81
	success := false // flip to true after all sub-system inits succeed
	defer func() {
82 83
		if !success && n != nil {
			n.Close()
84 85
		}
	}()
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
86

Juan Batiz-Benet's avatar
go fmt  
Juan Batiz-Benet committed
87
	if cfg == nil {
Brian Tiger Chow's avatar
Brian Tiger Chow committed
88
		return nil, debugerror.Errorf("configuration required")
Juan Batiz-Benet's avatar
go fmt  
Juan Batiz-Benet committed
89 90
	}

91
	n = &IpfsNode{
Brian Tiger Chow's avatar
Brian Tiger Chow committed
92 93
		onlineMode: online,
		Config:     cfg,
94
	}
95 96
	n.ContextGroup = ctxgroup.WithContextAndTeardown(ctx, n.teardown)
	ctx = n.ContextGroup.Context()
97

98 99 100
	// setup Peerstore
	n.Peerstore = peer.NewPeerstore()

101 102
	// setup datastore.
	if n.Datastore, err = makeDatastore(cfg.Datastore); err != nil {
Brian Tiger Chow's avatar
Brian Tiger Chow committed
103
		return nil, debugerror.Wrap(err)
Juan Batiz-Benet's avatar
go fmt  
Juan Batiz-Benet committed
104 105
	}

106 107 108
	// setup local peer ID (private key is loaded in online setup)
	if err := n.loadID(); err != nil {
		return nil, err
109 110
	}

111 112 113
	n.Blockstore, err = bstore.WriteCached(bstore.NewBlockstore(n.Datastore), kSizeBlockstoreWriteCache)
	if err != nil {
		return nil, debugerror.Wrap(err)
114 115
	}

116
	// setup online services
117
	if online {
118 119
		if err := n.StartOnlineServices(); err != nil {
			return nil, err // debugerror.Wraps.
120
		}
121 122
	} else {
		n.Exchange = offline.Exchange(n.Blockstore)
123
	}
124

125
	n.Blocks, err = bserv.New(n.Blockstore, n.Exchange)
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
126
	if err != nil {
Brian Tiger Chow's avatar
Brian Tiger Chow committed
127
		return nil, debugerror.Wrap(err)
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
128 129
	}

130 131
	n.DAG = merkledag.NewDAGService(n.Blocks)
	n.Pinning, err = pin.LoadPinner(n.Datastore, n.DAG)
Jeromy's avatar
Jeromy committed
132
	if err != nil {
133
		n.Pinning = pin.NewPinner(n.Datastore, n.DAG)
Jeromy's avatar
Jeromy committed
134
	}
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
135 136
	n.Resolver = &path.Resolver{DAG: n.DAG}

137
	success = true
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
138
	return n, nil
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
139
}
140

141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201
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
	}

	// setup the network
	listenAddrs, err := listenAddresses(n.Config)
	if err != nil {
		return debugerror.Wrap(err)
	}
	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)

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

Brian Tiger Chow's avatar
Brian Tiger Chow committed
202 203 204 205 206
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
207 208
}

Brian Tiger Chow's avatar
Brian Tiger Chow committed
209 210
func (n *IpfsNode) OnlineMode() bool {
	return n.onlineMode
Brian Tiger Chow's avatar
Brian Tiger Chow committed
211 212
}

213 214 215
func (n *IpfsNode) loadID() error {
	if n.Identity != "" {
		return debugerror.New("identity already loaded")
216 217
	}

218 219 220 221 222 223
	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?)")
224 225
	}

226 227 228
	n.Identity = peer.ID(b58.Decode(cid))
	return nil
}
229

230 231 232
func (n *IpfsNode) loadPrivateKey() error {
	if n.Identity == "" || n.Peerstore == nil {
		return debugerror.New("loaded private key out of order.")
233 234
	}

235 236 237 238 239
	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
240
	if err != nil {
241
		return err
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
242
	}
243

244 245 246
	n.PrivateKey = sk
	n.Peerstore.AddPrivKey(n.Identity, n.PrivateKey)
	return nil
247 248 249 250
}

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

255 256 257 258
	id2, err := peer.IDFromPrivateKey(sk)
	if err != nil {
		return nil, err
	}
259

260 261
	if id2 != id {
		return nil, fmt.Errorf("private key in config does not match id: %s != %s", id, id2)
262 263
	}

264
	return sk, nil
265
}
266

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

269 270 271 272 273
	var err error
	listen := make([]ma.Multiaddr, len(cfg.Addresses.Swarm))
	for i, addr := range cfg.Addresses.Swarm {

		listen[i], err = ma.NewMultiaddr(addr)
274
		if err != nil {
275
			return nil, fmt.Errorf("Failure to parse config.Addresses.Swarm[%d]: %s", i, cfg.Addresses.Swarm)
276 277 278 279 280
		}
	}

	return listen, nil
}