Skip to content

Commit 4d9d8c9

Browse files
committed
Merge pull request libp2p#10 from diasdavid/revisit
Revisit Swarm - multitransport + upgrades - https://github.com/diasdavid/node-ipfs-swarm/issues/8
2 parents 7aae025 + 1ba8e80 commit 4d9d8c9

File tree

10 files changed

+776
-491
lines changed

10 files changed

+776
-491
lines changed

README.md

+33-46
Original file line numberDiff line numberDiff line change
@@ -1,82 +1,69 @@
1-
ipfs-swarm Node.js implementation
1+
libp2p-swarm Node.js implementation
22
=================================
33

44
[![](https://img.shields.io/badge/made%20by-Protocol%20Labs-blue.svg?style=flat-square)](http://ipn.io) [![](https://img.shields.io/badge/project-IPFS-blue.svg?style=flat-square)](http://ipfs.io/) [![](https://img.shields.io/badge/freenode-%23ipfs-blue.svg?style=flat-square)](http://webchat.freenode.net/?channels=%23ipfs) [![Build Status](https://img.shields.io/travis/diasdavid/node-ipfs-swarm/master.svg?style=flat-square)](https://travis-ci.org/diasdavid/node-ipfs-swarm)
55

6-
> IPFS swarm implementation in Node.js
6+
> libp2p swarm implementation in Node.js
77
88
# Description
99

10-
ipfs-swarm is an abstraction for the network layer on IPFS. It offers an API to open streams between peers on a specific protocol.
10+
libp2p-swarm is a connection abstraction that is able to leverage several transports and connection upgrades, such as congestion control, channel encryption, multiplexing several streams in one connection, and more. It does this by bringing protocol multiplexing to the application level (instead of the traditional Port level) using multicodec and multistream.
1111

12-
Ref spec (WIP) - https://github.com/diasdavid/specs/blob/protocol-spec/protocol/layers.md#network-layer
12+
libp2p-swarm is used by libp2p but it can be also used as a standalone module.
1313

1414
# Usage
1515

16-
### Create a new Swarm
16+
### Install and create a Swarm
1717

18-
```javascript
19-
var Swarm = require('ipfs-swarm')
18+
libp2p-swarm is available on npm and so, like any other npm module, just:
2019

21-
var s = new Swarm([port]) // `port` defalts to 4001
20+
```bash
21+
$ npm install libp2p-swarm --save
2222
```
2323

24-
### Set the swarm to listen for incoming streams
24+
And use it in your Node.js code as:
2525

26-
```javascript
27-
s.listen([port], [callback]) // `port` defaults to 4001, `callback` gets called when the socket starts listening
28-
```
29-
30-
### Close the listener/socket and every open stream that was multiplexed on it
26+
```JavaScript
27+
var Swarm = require('libp2p-swarm')
3128

32-
```javascript
33-
s.closeListener()
29+
var sw = new Swarm(peerInfoSelf)
3430
```
3531

36-
### Register a protocol to be handled by an incoming stream
37-
38-
```javascript
39-
s.registerHandler('/name/protocol/you/want/version', function (stream) {})
40-
```
32+
peerInfoSelf is a [PeerInfo](https://github.com/diasdavid/node-peer-info) object that represents the peer creating this swarm instance.
4133

42-
### Open a new connection
34+
### Support a transport
4335

44-
Used when we want to make sure we can connect to a given peer, but do not intend to establish a stream with any of the services offered right away.
36+
libp2p-swarm expects transports that implement [abstract-transport](https://github.com/diasdavid/abstract-transport). For example [libp2p-tcp](https://github.com/diasdavid/node-libp2p-tcp), a simple shim on top of the `net` module to make it work with swarm expectations.
4537

46-
```
47-
s.openConnection(peerConnection, function (err) {})
38+
```JavaScript
39+
sw.addTransport(transport, [options, dialOptions, listenOptions])
4840
```
4941

42+
### Add a connection upgrade
5043

51-
### Dial a new stream
44+
A connection upgrade must be able to receive and return something that implements the [abstract-connection](https://github.com/diasdavid/abstract-connection) interface.
5245

46+
```JavaScript
47+
sw.addUpgrade(connUpgrade, [options])
5348
```
54-
s.openStream(peerInfo, protocol, function (err, stream) {})
55-
```
56-
57-
peerInfo must be a [`ipfs-peer`](https://www.npmjs.com/package/ipfs-peer) object, contaning both peer-id and multiaddrs.
5849

59-
## Events emitted
50+
Upgrading a connection to use a stream muxer is still considered an upgrade, but a special case since once this connection is applied, the returned obj will implement the [abstract-stream-muxer](https://github.com/diasdavid/abstract-stream-muxer) interface.
6051

52+
```JavaScript
53+
sw.addStreamMuxer(streamMuxer, [options])
6154
```
62-
.on('error')
6355

64-
.on('connection')
65-
.on('connection-unknown') // used by Identify to start the Identify protocol from listener to dialer
66-
```
67-
68-
## Identify protocol
69-
70-
The Identify protocol is an integral part to Swarm. It enables peers to share observedAddrs, identities and other possible address available. This enables us to do better NAT traversal.
71-
72-
To instantiate Identify:
56+
### Dial to another peer
7357

58+
```JavaScript
59+
sw.dial(PeerInfo, options, protocol)
60+
sw.dial(PeerInfo, options)
7461
```
75-
var Identify = require('ipfs-swarm/identify')
7662

77-
var i = new Identify(swarmInstance, peerSelf)
78-
```
63+
dial uses the best transport (whatever works first, in the future we can have some criteria), and jump starts the connection until the point we have to negotiate the protocol. If a muxer is available, then drop the muxer onto that connection. Good to warm up connections or to check for connectivity. If we have already a muxer for that peerInfo, than do nothing.
7964

80-
`swarmInstance` must be an Instance of swarm and `peerSelf` must be a instance of `ipfs-peer` that represents the peer that instantiated this Identify
65+
### Accept requests on a specific protocol
8166

82-
Identify emits a `peer-update` event each time it receives information from another peer.
67+
```JavaScript
68+
sw.handleProtocol(protocol, handlerFunction)
69+
```
File renamed without changes.

examples/peerB.js

+14
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
var Swarm = require('./../src')
2+
3+
var Peer = require('peer-info')
4+
var Id = require('peer-id')
5+
var multiaddr = require('multiaddr')
6+
var tcp = require('libp2p-tcp')
7+
8+
var mh = multiaddr('/ip4/127.0.0.1/tcp/8010')
9+
var p = new Peer(Id.create(), [])
10+
var sw = new Swarm(p)
11+
12+
sw.addTransport('tcp', tcp, { multiaddr: mh }, {}, {port: 8010}, function () {
13+
console.log('transport added')
14+
})

examples/s.js

-28
This file was deleted.

package.json

+10-9
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,12 @@
11
{
2-
"name": "ipfs-swarm",
2+
"name": "libp2p-swarm",
33
"version": "0.4.1",
44
"description": "IPFS swarm implementation in Node.js",
55
"main": "src/index.js",
66
"scripts": {
77
"test": "./node_modules/.bin/lab tests/*-test.js",
8-
"coverage": "./node_modules/.bin/lab -t 100 tests/*-test.js",
9-
"codestyle": "./node_modules/.bin/standard --format",
10-
"lint": "./node_modules/.bin/standard",
11-
"validate": "npm ls"
8+
"coverage": "./node_modules/.bin/lab -t 88 tests/*-test.js",
9+
"lint": "./node_modules/.bin/standard"
1210
},
1311
"repository": {
1412
"type": "git",
@@ -24,15 +22,18 @@
2422
},
2523
"homepage": "https://github.com/diasdavid/node-ipfs-swarm",
2624
"pre-commit": [
27-
"codestyle",
28-
"test"
25+
"lint",
26+
"test",
27+
"coverage"
2928
],
3029
"engines": {
3130
"node": "^4.0.0"
3231
},
3332
"devDependencies": {
3433
"code": "^1.4.1",
3534
"lab": "^5.13.0",
35+
"libp2p-spdy": "^0.1.0",
36+
"libp2p-tcp": "^0.1.1",
3637
"precommit-hook": "^3.0.0",
3738
"sinon": "^1.15.4",
3839
"standard": "^4.5.2",
@@ -42,11 +43,11 @@
4243
"async": "^1.3.0",
4344
"ip-address": "^4.0.0",
4445
"ipfs-logger": "^0.1.0",
45-
"ipfs-peer": "^0.3.0",
46-
"ipfs-peer-id": "^0.3.0",
4746
"multiaddr": "^1.0.0",
4847
"multiplex-stream-muxer": "^0.2.0",
4948
"multistream-select": "^0.6.1",
49+
"peer-id": "^0.3.3",
50+
"peer-info": "^0.3.2",
5051
"protocol-buffers-stream": "^1.2.0",
5152
"spdy-stream-muxer": "^0.6.0"
5253
}

src/identify/index.js

+79-62
Original file line numberDiff line numberDiff line change
@@ -4,93 +4,110 @@
44
*/
55

66
var Interactive = require('multistream-select').Interactive
7-
var EventEmmiter = require('events').EventEmitter
8-
var util = require('util')
97
var protobufs = require('protocol-buffers-stream')
108
var fs = require('fs')
119
var schema = fs.readFileSync(__dirname + '/identify.proto')
1210
var v6 = require('ip-address').v6
13-
var Id = require('ipfs-peer-id')
11+
var Id = require('peer-id')
1412
var multiaddr = require('multiaddr')
1513

16-
exports = module.exports = Identify
14+
exports = module.exports = identify
1715

18-
util.inherits(Identify, EventEmmiter)
16+
var protoId = '/ipfs/identify/1.0.0'
1917

20-
function Identify (swarm, peerSelf) {
21-
var self = this
22-
self.createProtoStream = protobufs(schema)
18+
exports.protoId = protoId
19+
var createProtoStream = protobufs(schema)
2320

24-
swarm.registerHandler('/ipfs/identify/1.0.0', function (stream) {
25-
var ps = self.createProtoStream()
21+
function identify (muxedConns, peerInfoSelf, socket, conn, muxer) {
22+
var msi = new Interactive()
23+
msi.handle(conn, function () {
24+
msi.select(protoId, function (err, ds) {
25+
if (err) {
26+
return console.log(err) // TODO Treat error
27+
}
2628

27-
ps.on('identify', function (msg) {
28-
updateSelf(peerSelf, msg.observedAddr)
29+
var ps = createProtoStream()
2930

30-
var peerId = Id.createFromPubKey(msg.publicKey)
31+
ps.on('identify', function (msg) {
32+
var peerId = Id.createFromPubKey(msg.publicKey)
33+
34+
updateSelf(peerInfoSelf, msg.observedAddr)
35+
36+
muxedConns[peerId.toB58String()] = {
37+
muxer: muxer,
38+
socket: socket
39+
}
40+
41+
// TODO: Pass the new discovered info about the peer that contacted us
42+
// to something like the Kademlia Router, so the peerInfo for this peer
43+
// is fresh
44+
// - before this was exectued through a event emitter
45+
// self.emit('peer-update', {
46+
// peerId: peerId,
47+
// listenAddrs: msg.listenAddrs.map(function (mhb) {return multiaddr(mhb)})
48+
// })
49+
})
3150

32-
var socket = swarm.connections[peerId.toB58String()].socket
3351
var mh = getMultiaddr(socket)
52+
3453
ps.identify({
3554
protocolVersion: 'na',
3655
agentVersion: 'na',
37-
publicKey: peerSelf.id.pubKey,
38-
listenAddrs: peerSelf.multiaddrs.map(function (mh) {return mh.buffer}),
56+
publicKey: peerInfoSelf.id.pubKey,
57+
listenAddrs: peerInfoSelf.multiaddrs.map(function (mh) {
58+
return mh.buffer
59+
}),
3960
observedAddr: mh.buffer
4061
})
4162

42-
self.emit('peer-update', {
43-
peerId: peerId,
44-
listenAddrs: msg.listenAddrs.map(function (mhb) {return multiaddr(mhb)})
45-
})
46-
63+
ps.pipe(ds).pipe(ps)
4764
ps.finalize()
4865
})
49-
ps.pipe(stream).pipe(ps)
5066
})
67+
}
68+
69+
exports.getHandlerFunction = function (peerInfoSelf, muxedConns) {
70+
return function (conn) {
71+
// wait for the other peer to identify itself
72+
// update our multiaddr with observed addr list
73+
// then get the socket from our list of muxedConns and send the reply back
74+
75+
var ps = createProtoStream()
76+
77+
ps.on('identify', function (msg) {
78+
updateSelf(peerInfoSelf, msg.observedAddr)
5179

52-
swarm.on('connection-unknown', function (conn, socket) {
53-
conn.dialStream(function (err, stream) {
54-
if (err) { return console.log(err) }
55-
var msi = new Interactive()
56-
msi.handle(stream, function () {
57-
msi.select('/ipfs/identify/1.0.0', function (err, ds) {
58-
if (err) { return console.log(err) }
59-
60-
var ps = self.createProtoStream()
61-
62-
ps.on('identify', function (msg) {
63-
var peerId = Id.createFromPubKey(msg.publicKey)
64-
65-
updateSelf(peerSelf, msg.observedAddr)
66-
67-
swarm.connections[peerId.toB58String()] = {
68-
conn: conn,
69-
socket: socket
70-
}
71-
72-
self.emit('peer-update', {
73-
peerId: peerId,
74-
listenAddrs: msg.listenAddrs.map(function (mhb) {return multiaddr(mhb)})
75-
})
76-
})
77-
78-
var mh = getMultiaddr(socket)
79-
80-
ps.identify({
81-
protocolVersion: 'na',
82-
agentVersion: 'na',
83-
publicKey: peerSelf.id.pubKey,
84-
listenAddrs: peerSelf.multiaddrs.map(function (mh) {return mh.buffer}),
85-
observedAddr: mh.buffer
86-
})
87-
88-
ps.pipe(ds).pipe(ps)
89-
ps.finalize()
90-
})
80+
var peerId = Id.createFromPubKey(msg.publicKey)
81+
82+
var socket = muxedConns[peerId.toB58String()].socket
83+
84+
var mh = getMultiaddr(socket)
85+
86+
ps.identify({
87+
protocolVersion: 'na',
88+
agentVersion: 'na',
89+
publicKey: peerInfoSelf.id.pubKey,
90+
listenAddrs: peerInfoSelf.multiaddrs.map(function (mh) {
91+
return mh.buffer
92+
}),
93+
observedAddr: mh.buffer
9194
})
95+
96+
// TODO: Pass the new discovered info about the peer that contacted us
97+
// to something like the Kademlia Router, so the peerInfo for this peer
98+
// is fresh
99+
// - before this was exectued through a event emitter
100+
// self.emit('peer-update', {
101+
// peerId: peerId,
102+
// listenAddrs: msg.listenAddrs.map(function (mhb) {
103+
// return multiaddr(mhb)
104+
// })
105+
// })
106+
107+
ps.finalize()
92108
})
93-
})
109+
ps.pipe(conn).pipe(ps)
110+
}
94111
}
95112

96113
function getMultiaddr (socket) {

src/stream-muxer.js

-2
This file was deleted.

0 commit comments

Comments
 (0)