Skip to content
This repository was archived by the owner on Aug 23, 2019. It is now read-only.

Commit 546c9bc

Browse files
authored
Merge pull request #67 from libp2p/secio
[WIP] Add secio & move to pull-streams
2 parents 6cbcf23 + 6091e94 commit 546c9bc

20 files changed

+486
-29879
lines changed

README.md

+43-6
Original file line numberDiff line numberDiff line change
@@ -59,13 +59,13 @@ const sw = new Swarm(peerInfo)
5959

6060
## API
6161

62-
peerInfo is a [PeerInfo](https://github.com/diasdavid/js-peer-info) object that represents the peer creating this swarm instance.
62+
peerInfo is a [PeerInfo](https://github.com/libp2p/js-peer-info) object that represents the peer creating this swarm instance.
6363

6464
### Transports
6565

6666
##### `swarm.transport.add(key, transport, options, callback)`
6767

68-
libp2p-swarm expects transports that implement [interface-transport](https://github.com/diasdavid/abstract-transport). For example [libp2p-tcp](https://github.com/diasdavid/js-libp2p-tcp).
68+
libp2p-swarm expects transports that implement [interface-transport](https://github.com/libp2p/abstract-transport). For example [libp2p-tcp](https://github.com/libp2p/js-libp2p-tcp).
6969

7070
- `key` - the transport identifier.
7171
- `transport` -
@@ -100,20 +100,31 @@ Close the listeners of a given transport.
100100

101101
##### `swarm.connection.addUpgrade()`
102102

103-
A connection upgrade must be able to receive and return something that implements the [interface-connection](https://github.com/diasdavid/interface-connection) specification.
103+
A connection upgrade must be able to receive and return something that implements the [interface-connection](https://github.com/libp2p/interface-connection) specification.
104104

105105
> **WIP**
106106
107107
##### `swarm.connection.addStreamMuxer(muxer)`
108108

109-
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 [interface-stream-muxer](https://github.com/diasdavid/interface-stream-muxer) spec.
109+
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 [interface-stream-muxer](https://github.com/libp2p/interface-stream-muxer) spec.
110110

111111
- `muxer`
112112

113113
##### `swarm.connection.reuse()`
114114

115115
Enable the identify protocol.
116116

117+
##### `swarm.connection.crypto([tag, encrypt])`
118+
119+
Enable a specified crypto protocol. By default no encryption is used, aka `plaintext`. If called with no arguments it resets to use `plaintext`.
120+
121+
You can use for example [libp2p-secio](https://github.com/libp2p/js-libp2p-secio) like this
122+
123+
```js
124+
const secio = require('libp2p-secio')
125+
swarm.connection.crypto(secio.tag, secio.encrypt)
126+
```
127+
117128
### `swarm.dial(pi, protocol, callback)`
118129

119130
dial uses the best transport (whatever works first, in the future we can have some criteria), and jump starts the connection until the point where 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, then do nothing.
@@ -152,17 +163,43 @@ Close all the listeners and muxers.
152163

153164
- `callback`
154165

166+
### This module uses `pull-streams`
167+
168+
We expose a streaming interface based on `pull-streams`, rather then on the Node.js core streams implementation (aka Node.js streams). `pull-streams` offers us a better mechanism for error handling and flow control guarantees. If you would like to know more about why we did this, see the discussion at this [issue](https://github.com/ipfs/js-ipfs/issues/362).
169+
170+
You can learn more about pull-streams at:
171+
172+
- [The history of Node.js streams, nodebp April 2014](https://www.youtube.com/watch?v=g5ewQEuXjsQ)
173+
- [The history of streams, 2016](http://dominictarr.com/post/145135293917/history-of-streams)
174+
- [pull-streams, the simple streaming primitive](http://dominictarr.com/post/149248845122/pull-streams-pull-streams-are-a-very-simple)
175+
- [pull-streams documentation](https://pull-stream.github.io/)
176+
177+
#### Converting `pull-streams` to Node.js Streams
178+
179+
If you are a Node.js streams user, you can convert a pull-stream to a Node.js stream using the module [`pull-stream-to-stream`](https://github.com/dominictarr/pull-stream-to-stream), giving you an instance of a Node.js stream that is linked to the pull-stream. For example:
180+
181+
```js
182+
const pullToStream = require('pull-stream-to-stream')
183+
184+
const nodeStreamInstance = pullToStream(pullStreamInstance)
185+
// nodeStreamInstance is an instance of a Node.js Stream
186+
```
187+
188+
To learn more about this utility, visit https://pull-stream.github.io/#pull-stream-to-stream.
189+
190+
191+
155192
## Design
156193

157194
### Multitransport
158195

159196
libp2p is designed to support multiple transports at the same time. While peers are identified by their ID (which are generated from their public keys), the addresses of each pair may vary, depending the device where they are being run or the network in which they are accessible through.
160197

161-
In order for a transport to be supported, it has to follow the [interface-transport](https://github.com/diasdavid/interface-transport) spec.
198+
In order for a transport to be supported, it has to follow the [interface-transport](https://github.com/libp2p/interface-transport) spec.
162199

163200
### Connection upgrades
164201

165-
Each connection in libp2p follows the [interface-connection](https://github.com/diasdavid/interface-connection) spec. This design decision enables libp2p to have upgradable transports.
202+
Each connection in libp2p follows the [interface-connection](https://github.com/libp2p/interface-connection) spec. This design decision enables libp2p to have upgradable transports.
166203

167204
We think of `upgrade` as a very important notion when we are talking about connections, we can see mechanisms like: stream multiplexing, congestion control, encrypted channels, multipath, simulcast, etc, as `upgrades` to a connection. A connection can be a simple and with no guarantees, drop a packet on the network with a destination thing, a transport in the other hand can be a connection and or a set of different upgrades that are mounted on top of each other, giving extra functionality to that connection and therefore `upgrading` it.
168205

gulpfile.js

+2-1
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ const gulp = require('gulp')
44
const PeerInfo = require('peer-info')
55
const PeerId = require('peer-id')
66
const WebSockets = require('libp2p-websockets')
7+
const pull = require('pull-stream')
78

89
const Swarm = require('./src')
910
const spdy = require('libp2p-spdy')
@@ -60,7 +61,7 @@ gulp.task('test:browser:before', (done) => {
6061
sigS = sigServer.start(15555, ready)
6162

6263
function echo (conn) {
63-
conn.pipe(conn)
64+
pull(conn, conn)
6465
}
6566
})
6667

package.json

+18-19
Original file line numberDiff line numberDiff line change
@@ -37,37 +37,36 @@
3737
"node": "^4.3.0"
3838
},
3939
"devDependencies": {
40-
"aegir": "^4.0.0",
40+
"aegir": "^8.0.0",
4141
"buffer-loader": "0.0.1",
4242
"chai": "^3.5.0",
4343
"gulp": "^3.9.1",
44-
"istanbul": "^0.4.3",
4544
"libp2p-multiplex": "^0.2.1",
46-
"libp2p-spdy": "^0.8.1",
47-
"libp2p-tcp": "^0.7.4",
48-
"libp2p-webrtc-star": "^0.3.2",
49-
"libp2p-websockets": "^0.7.1",
50-
"pre-commit": "^1.1.2",
51-
"stream-pair": "^1.0.3",
45+
"libp2p-secio": "^0.4.2",
46+
"libp2p-spdy": "^0.9.0",
47+
"libp2p-tcp": "^0.8.1",
48+
"libp2p-webrtc-star": "^0.4.4",
49+
"libp2p-websockets": "^0.8.1",
50+
"pre-commit": "^1.1.3",
51+
"pull-goodbye": "0.0.1",
52+
"pull-stream": "^3.4.5",
5253
"webrtcsupport": "^2.2.0"
5354
},
5455
"dependencies": {
55-
"babel-runtime": "^6.6.1",
56-
"bl": "^1.1.2",
56+
"babel-runtime": "^6.11.6",
5757
"browserify-zlib": "github:ipfs/browserify-zlib",
5858
"debug": "^2.2.0",
59-
"duplexify": "^3.4.3",
60-
"interface-connection": "^0.1.7",
61-
"ip-address": "^5.8.0",
62-
"length-prefixed-stream": "^1.5.0",
63-
"libp2p-identify": "^0.1.3",
59+
"interface-connection": "^0.2.1",
60+
"ip-address": "^5.8.2",
61+
"libp2p-identify": "^0.2.0",
6462
"lodash.contains": "^2.4.3",
65-
"multiaddr": "^2.0.0",
66-
"multistream-select": "^0.9.0",
63+
"multiaddr": "^2.0.2",
64+
"multistream-select": "^0.11.0",
6765
"peer-id": "^0.7.0",
6866
"peer-info": "^0.7.0",
6967
"protocol-buffers": "^3.1.6",
70-
"run-parallel": "^1.1.6"
68+
"run-parallel": "^1.1.6",
69+
"run-waterfall": "^1.1.3"
7170
},
7271
"contributors": [
7372
"David Dias <[email protected]>",
@@ -77,4 +76,4 @@
7776
"Richard Littauer <[email protected]>",
7877
"dignifiedquire <[email protected]>"
7978
]
80-
}
79+
}

src/connection.js

+40-19
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,13 @@
11
'use strict'
22

3-
const protocolMuxer = require('./protocol-muxer')
43
const identify = require('libp2p-identify')
54
const multistream = require('multistream-select')
5+
const waterfall = require('run-waterfall')
6+
const debug = require('debug')
7+
const log = debug('libp2p:swarm:connection')
8+
9+
const protocolMuxer = require('./protocol-muxer')
10+
const plaintext = require('./plaintext')
611

712
module.exports = function connection (swarm) {
813
return {
@@ -14,7 +19,7 @@ module.exports = function connection (swarm) {
1419

1520
// for listening
1621
swarm.handle(muxer.multicodec, (conn) => {
17-
const muxedConn = muxer(conn, true)
22+
const muxedConn = muxer.listener(conn)
1823

1924
muxedConn.on('stream', (conn) => {
2025
protocolMuxer(swarm.protocols, conn)
@@ -29,28 +34,23 @@ module.exports = function connection (swarm) {
2934
conn.getPeerInfo = (cb) => {
3035
const conn = muxedConn.newStream()
3136
const ms = new multistream.Dialer()
32-
ms.handle(conn, (err) => {
33-
if (err) { return cb(err) }
34-
35-
ms.select(identify.multicodec, (err, conn) => {
36-
if (err) { return cb(err) }
3737

38-
identify.exec(conn, (err, peerInfo, observedAddrs) => {
39-
if (err) { return cb(err) }
40-
41-
observedAddrs.forEach((oa) => {
42-
swarm._peerInfo.multiaddr.addSafe(oa)
43-
})
44-
45-
cb(null, peerInfo)
38+
waterfall([
39+
(cb) => ms.handle(conn, cb),
40+
(cb) => ms.select(identify.multicodec, cb),
41+
(conn, cb) => identify.dialer(conn, cb),
42+
(peerInfo, observedAddrs, cb) => {
43+
observedAddrs.forEach((oa) => {
44+
swarm._peerInfo.multiaddr.addSafe(oa)
4645
})
47-
})
48-
})
46+
cb(null, peerInfo)
47+
}
48+
], cb)
4949
}
5050

5151
conn.getPeerInfo((err, peerInfo) => {
5252
if (err) {
53-
return console.log('Identify not successful')
53+
return log('Identify not successful')
5454
}
5555
swarm.muxedConns[peerInfo.id.toB58String()] = {
5656
muxer: muxedConn
@@ -63,12 +63,33 @@ module.exports = function connection (swarm) {
6363
})
6464
})
6565
}
66+
67+
return conn
6668
})
6769
},
6870

6971
reuse () {
7072
swarm.identify = true
71-
swarm.handle(identify.multicodec, identify.handler(swarm._peerInfo))
73+
swarm.handle(identify.multicodec, (conn) => {
74+
identify.listener(conn, swarm._peerInfo)
75+
})
76+
},
77+
78+
crypto (tag, encrypt) {
79+
if (!tag && !encrypt) {
80+
tag = plaintext.tag
81+
encrypt = plaintext.encrypt
82+
}
83+
84+
swarm.unhandle(swarm.crypto.tag)
85+
swarm.handle(tag, (conn) => {
86+
const id = swarm._peerInfo.id
87+
const secure = encrypt(id, id.privKey, conn)
88+
89+
protocolMuxer(swarm.protocols, secure)
90+
})
91+
92+
swarm.crypto = {tag, encrypt}
7293
}
7394
}
7495
}

src/dial.js

+16-4
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22

33
const multistream = require('multistream-select')
44
const Connection = require('interface-connection').Connection
5+
const debug = require('debug')
6+
const log = debug('libp2p:swarm:dial')
57

68
const protocolMuxer = require('./protocol-muxer')
79

@@ -19,6 +21,7 @@ module.exports = function dial (swarm) {
1921
const proxyConn = new Connection()
2022

2123
const b58Id = pi.id.toB58String()
24+
log('dialing %s', b58Id)
2225

2326
if (!swarm.muxedConns[b58Id]) {
2427
if (!swarm.conns[b58Id]) {
@@ -44,7 +47,6 @@ module.exports = function dial (swarm) {
4447

4548
function gotWarmedUpConn (conn) {
4649
conn.setPeerInfo(pi)
47-
4850
attemptMuxerUpgrade(conn, (err, muxer) => {
4951
if (!protocol) {
5052
if (err) {
@@ -97,13 +99,22 @@ module.exports = function dial (swarm) {
9799
cryptoDial()
98100

99101
function cryptoDial () {
100-
// currently, no crypto channel is implemented
101102
const ms = new multistream.Dialer()
102103
ms.handle(conn, (err) => {
103104
if (err) {
104105
return cb(err)
105106
}
106-
ms.select('/plaintext/1.0.0', cb)
107+
108+
const id = swarm._peerInfo.id
109+
log('selecting crypto: %s', swarm.crypto.tag)
110+
ms.select(swarm.crypto.tag, (err, conn) => {
111+
if (err) {
112+
return cb(err)
113+
}
114+
115+
const wrapped = swarm.crypto.encrypt(id, id.privKey, conn)
116+
cb(null, wrapped)
117+
})
107118
})
108119
}
109120
})
@@ -129,6 +140,7 @@ module.exports = function dial (swarm) {
129140
if (err) {
130141
return callback(new Error('multistream not supported'))
131142
}
143+
log('selecting %s', key)
132144
ms.select(key, (err, conn) => {
133145
if (err) {
134146
if (muxers.length === 0) {
@@ -139,7 +151,7 @@ module.exports = function dial (swarm) {
139151
return
140152
}
141153

142-
const muxedConn = swarm.muxers[key](conn, false)
154+
const muxedConn = swarm.muxers[key].dialer(conn)
143155
swarm.muxedConns[b58Id] = {}
144156
swarm.muxedConns[b58Id].muxer = muxedConn
145157
// should not be needed anymore - swarm.muxedConns[b58Id].conn = conn

src/index.js

+15-11
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ const transport = require('./transport')
99
const connection = require('./connection')
1010
const dial = require('./dial')
1111
const protocolMuxer = require('./protocol-muxer')
12+
const plaintext = require('./plaintext')
1213

1314
exports = module.exports = Swarm
1415

@@ -50,6 +51,9 @@ function Swarm (peerInfo) {
5051
// is the Identify protocol enabled?
5152
this.identify = false
5253

54+
// Crypto details
55+
this.crypto = plaintext
56+
5357
this.transport = transport(this)
5458
this.connection = connection(this)
5559

@@ -90,12 +94,13 @@ function Swarm (peerInfo) {
9094
this.protocols[protocol] = handler
9195
}
9296

93-
// our crypto handshake :)
94-
this.handle('/plaintext/1.0.0', (conn) => {
95-
protocolMuxer(this.protocols, conn)
97+
this.handle(this.crypto.tag, (conn) => {
98+
const id = this._peerInfo.id
99+
const wrapped = this.crypto.encrypt(id, id.privKey, conn)
100+
return protocolMuxer(this.protocols, wrapped)
96101
})
97102

98-
this.unhandle = (protocol, handler) => {
103+
this.unhandle = (protocol) => {
99104
if (this.protocols[protocol]) {
100105
delete this.protocols[protocol]
101106
}
@@ -122,14 +127,13 @@ function Swarm (peerInfo) {
122127

123128
const transports = this.transports
124129

125-
parallel(Object.keys(transports).map((key) => {
126-
return (cb) => {
130+
parallel(
131+
Object.keys(transports).map((key) => (cb) => {
127132
parallel(transports[key].listeners.map((listener) => {
128-
return (cb) => {
129-
listener.close(cb)
130-
}
133+
return (cb) => listener.close(cb)
131134
}), cb)
132-
}
133-
}), callback)
135+
}),
136+
callback
137+
)
134138
}
135139
}

0 commit comments

Comments
 (0)