core.go 7.67 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"
14
	ic "github.com/jbenet/go-ipfs/crypto"
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"
23
	inet "github.com/jbenet/go-ipfs/net"
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
24
	swarmnet "github.com/jbenet/go-ipfs/net/swarmnet"
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
25
	path "github.com/jbenet/go-ipfs/path"
Juan Batiz-Benet's avatar
go fmt  
Juan Batiz-Benet committed
26
	peer "github.com/jbenet/go-ipfs/peer"
Jeromy's avatar
Jeromy committed
27
	pin "github.com/jbenet/go-ipfs/pin"
28 29
	routing "github.com/jbenet/go-ipfs/routing"
	dht "github.com/jbenet/go-ipfs/routing/dht"
30
	ds2 "github.com/jbenet/go-ipfs/util/datastore2"
31
	debugerror "github.com/jbenet/go-ipfs/util/debugerror"
32
	eventlog "github.com/jbenet/go-ipfs/util/eventlog"
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
33 34
)

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

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

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

43 44
	// Self
	Config     *config.Config // the node's configuration
45 46
	Identity   peer.ID        // the local node's identity
	PrivateKey ic.PrivKey     // the local node's private Key
47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63
	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
	Network     inet.Network         // the network message stream
	Routing     routing.IpfsRouting  // the routing system. recommend ipfs-dht
	Exchange    exchange.Interface   // the block exchange + strategy (bitswap)
	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
64

65
	ctxgroup.ContextGroup
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
66 67
}

68 69 70 71 72 73 74 75
// 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
76
// NewIpfsNode constructs a new IpfsNode based on the given config.
77
func NewIpfsNode(ctx context.Context, cfg *config.Config, online bool) (n *IpfsNode, err error) {
78 79
	success := false // flip to true after all sub-system inits succeed
	defer func() {
80 81
		if !success && n != nil {
			n.Close()
82 83
		}
	}()
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
84

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

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

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

101 102
	// setup local peer identity
	n.Identity, n.PrivateKey, err = initIdentity(&n.Config.Identity, online)
103
	if err != nil {
Brian Tiger Chow's avatar
Brian Tiger Chow committed
104
		return nil, debugerror.Wrap(err)
105 106
	}

107 108 109 110 111 112
	// setup Peerstore
	n.Peerstore = peer.NewPeerstore()
	if n.PrivateKey != nil {
		n.Peerstore.AddPrivKey(n.Identity, n.PrivateKey)
	}

113 114
	blockstore, err := bstore.WriteCached(bstore.NewBlockstore(n.Datastore), kSizeBlockstoreWriteCache)
	n.Exchange = offline.Exchange(blockstore)
115

116
	// setup online services
117
	if online {
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
118

119
		// setup the network
120 121
		listenAddrs, err := listenAddresses(cfg)
		if err != nil {
Brian Tiger Chow's avatar
Brian Tiger Chow committed
122
			return nil, debugerror.Wrap(err)
123 124
		}

Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
125
		n.Network, err = swarmnet.NewNetwork(ctx, listenAddrs, n.Identity, n.Peerstore)
126
		if err != nil {
Brian Tiger Chow's avatar
Brian Tiger Chow committed
127
			return nil, debugerror.Wrap(err)
128
		}
129
		n.AddChildGroup(n.Network.CtxGroup())
130

131 132 133
		// 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.)
134 135 136 137 138 139
		addrs, err := n.Network.InterfaceListenAddresses()
		if err != nil {
			return nil, debugerror.Wrap(err)
		}

		n.Peerstore.AddAddresses(n.Identity, addrs)
140

141
		// setup diagnostics service
142
		n.Diagnostics = diag.NewDiagnostics(n.Identity, n.Network)
Jeromy's avatar
Jeromy committed
143

144
		// setup routing service
145
		dhtRouting := dht.NewDHT(ctx, n.Identity, n.Network, n.Datastore)
Jeromy's avatar
Jeromy committed
146
		dhtRouting.Validators[IpnsValidatorTag] = namesys.ValidateIpnsRecord
Jeromy's avatar
Jeromy committed
147

148
		// TODO(brian): perform this inside NewDHT factory method
149
		n.Routing = dhtRouting
150
		n.AddChildGroup(dhtRouting)
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
151

152
		// setup exchange service
153
		const alwaysSendToPeer = true // use YesManStrategy
154
		bitswapNetwork := bsnet.NewFromIpfsNetwork(n.Network, n.Routing)
155

156
		n.Exchange = bitswap.New(ctx, n.Identity, bitswapNetwork, blockstore, alwaysSendToPeer)
157

158
		// TODO consider moving connection supervision into the Network. We've
Brian Tiger Chow's avatar
Brian Tiger Chow committed
159 160 161 162 163
		// 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.
164
		go superviseConnections(ctx, n.Network, dhtRouting, n.Peerstore, n.Config.Bootstrap)
165
	}
166

167 168
	// TODO(brian): when offline instantiate the BlockService with a bitswap
	// session that simply doesn't return blocks
169
	n.Blocks, err = bserv.New(blockstore, n.Exchange)
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
170
	if err != nil {
Brian Tiger Chow's avatar
Brian Tiger Chow committed
171
		return nil, debugerror.Wrap(err)
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
172 173
	}

174 175 176
	n.DAG = merkledag.NewDAGService(n.Blocks)
	n.Namesys = namesys.NewNameSystem(n.Routing)
	n.Pinning, err = pin.LoadPinner(n.Datastore, n.DAG)
Jeromy's avatar
Jeromy committed
177
	if err != nil {
178
		n.Pinning = pin.NewPinner(n.Datastore, n.DAG)
Jeromy's avatar
Jeromy committed
179
	}
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
180 181
	n.Resolver = &path.Resolver{DAG: n.DAG}

182
	success = true
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
183
	return n, nil
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
184
}
185

Brian Tiger Chow's avatar
Brian Tiger Chow committed
186 187 188 189 190
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
191 192
}

Brian Tiger Chow's avatar
Brian Tiger Chow committed
193 194
func (n *IpfsNode) OnlineMode() bool {
	return n.onlineMode
Brian Tiger Chow's avatar
Brian Tiger Chow committed
195 196
}

197 198
func initIdentity(cfg *config.Identity, online bool) (peer.ID, ic.PrivKey, error) {

199
	if cfg.PeerID == "" {
200
		return "", nil, debugerror.New("Identity was not set in config (was ipfs init run?)")
201 202
	}

203
	if len(cfg.PeerID) == 0 {
204
		return "", nil, debugerror.New("No peer ID in config! (was ipfs init run?)")
205 206
	}

207
	id := peer.ID(b58.Decode(cfg.PeerID))
208 209 210 211 212 213 214

	// when not online, don't need to parse private keys (yet)
	if !online {
		return id, nil, nil
	}

	sk, err := loadPrivateKey(cfg, id)
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
215
	if err != nil {
216
		return "", nil, err
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
217
	}
218 219 220 221 222 223

	return id, sk, nil
}

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

228 229 230 231
	id2, err := peer.IDFromPrivateKey(sk)
	if err != nil {
		return nil, err
	}
232

233 234
	if id2 != id {
		return nil, fmt.Errorf("private key in config does not match id: %s != %s", id, id2)
235 236
	}

237
	return sk, nil
238
}
239

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

242 243 244 245 246
	var err error
	listen := make([]ma.Multiaddr, len(cfg.Addresses.Swarm))
	for i, addr := range cfg.Addresses.Swarm {

		listen[i], err = ma.NewMultiaddr(addr)
247
		if err != nil {
248
			return nil, fmt.Errorf("Failure to parse config.Addresses.Swarm[%d]: %s", i, cfg.Addresses.Swarm)
249 250 251 252 253
		}
	}

	return listen, nil
}